我们知道Java中HashSet是Set的一个子类,其主要特点是存储的元素无序且不重复。
这里先举个例子,来演示HashSet的一些特性:
/**
* @param args
* Set集合,无索引,不可以重复,无序(存取不一致)
*/
public static void demo1() {
HashSet<String> hs = new HashSet<>(); //创建HashSet对象
boolean b1 = hs.add("a");
boolean b2 = hs.add("a"); //当向set集合中存储重复元素的时候返回为false
hs.add("b");
hs.add("c");
hs.add("d");
System.out.println(hs); //HashSet的继承体系中有重写toString方法
System.out.println(b1);
System.out.println(b2);
for (String string : hs) { //只要能用迭代器迭代的,就可以使用增强for循环遍历
System.out.println(string);
}
}
打印机结果:
[d,b,c,a]
true
false
d
b
c
a
我们发现添加的字符串元素为a,a,b,c,d,打印确实d,b,c,a,的确是无序且唯一。接下来我们来看看HashSet存储自定义对象如何保证元素的唯一性。
我先定义了一个Person类,类成员变量有age和name,之后使用Hashset去添加几个person对象,例子如下:
/**
* 自定义类Person
*/
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
/**
* 添加几个person对象到hashSet中
*/
public static void demo1() {
HashSet<Person> hs = new HashSet<>();
hs.add(new Person("张三", 23));
hs.add(new Person("张三", 23));
hs.add(new Person("李四", 24));
hs.add(new Person("李四", 24));
hs.add(new Person("李四", 24));
hs.add(new Person("李四", 24));
System.out.println(hs.size());
System.out.println(hs);
}
打印结果:
6
[Person [name=李四,age=24],Person [name=李四,age=24],Person [name=李四,age=24],Person [name=李四,age=24],Person [name=张三,age=23],Person [name=张三,age=23]]
从打印结果我们可以看到HashSet将重复的name和age存进去了,原因很好解释,每当new一个新对象,会有一个新内存地址,而name和age只是对象内部的属性,内存地址没有重复,符合hashSet的唯一性这一特点。那我们的需求是只要同姓名同年龄的person对象,我们就认为它是同一对象。该怎么做呢?有的朋友可能会想到在Person类中重写equels方法,我们来尝试一下:
@Override
public boolean equals(Object obj) {
Person p = (Person)obj;
System.out.println("我被调用了");
return this.name.equals(p.name)&&this.age==p.age;
}
经过测试,发现重写的equels()方法并没有被调用,所以我们需要做的是能让equels()方法调用,才能实现只要同姓名同年龄的person对象,我们就认为它是同一对象的需求。
其实,HashSet中添加对象元素时判断其唯一性是通过一个叫做hashCode的方法决定,hashCode相同才会调用equels()。而在Person类中重写hashCode(),并将返回值设置为一个固定特殊的值,让每一个对象的hashCode都相同,才会触发重写的equels()方法,代码如下:
@Override
public boolean equals(Object obj) {
Person p = (Person)obj;
System.out.println("我被调用了");
return this.name.equals(p.name)&&this.age==p.age;
}
@Override
public boolean equals(Object obj) {
return 10;
}
打印结果:
我被调用了
我被调用了
我被调用了
我被调用了
我被调用了
[Person [name=李四,age=24],Person [name=张三,age=23]]
这时我们发现HashSet存储自定义对象保证元素的唯一性的问题就被解决了。
其实它的内部原理也非常简单
如果我们使用的开发工具是eclipse,直接alt+shift+s自动生成重写的hashCode()和equels()即可,使用自动生成的代码,还能有效减少调用equels()的次数,提高运行效率。