要求
建立Persons类,两个属性int id,String name,重写toString(),equals(),hashCode(),问下面代码输出什么?
public class HashSetTest {
public static void main(String[] args) {
HashSet<Persons> persons = new HashSet<>();
Persons a = new Persons(1001,"aaa");
Persons b = new Persons(1002,"bbb");
persons.add(a);
persons.add(b);
a.name = "ccc";
persons.remove(a);
System.out.println(persons);
persons.add(new Persons(1001,"ccc"));
System.out.println(persons);
persons.add(new Persons(1001,"aaa"));
System.out.println(persons);
}
}
class Persons{
public int id;
public String name;
public Persons(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Persons person = (Persons) o;
return id == person.id && name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public String toString() {
return "Persons{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
输出
[Persons{id=1001, name='ccc'}, Persons{id=1002, name='bbb'}]
[Persons{id=1001, name='ccc'}, Persons{id=1002, name='bbb'}, Persons{id=1001, name='ccc'}]
[Persons{id=1001, name='ccc'}, Persons{id=1002, name='bbb'}, Persons{id=1001, name='aaa'}, Persons{id=1001, name='ccc'}]
结论
这道题一不小心就会出错,其实考验的还是HashSet的去重机制,我们知道HashSet的去重机制就是底层HashMap的去重机制:hashCode+equals。
比如来了一个对象,先计算出hashCode再通过hashCode得到一个table的索引值,如果table该索引值上没用对象,则将新对象加入进去,如果已经存在对象,则根据对象的equals方法判断是否相同,相同则进行value值替换,因为HashSet的value是一个默认相同的值,则表示此处不会更改。如果判断不相同,则继续判断链表后续的对象,有相同的则继续前面方式的值替换,都不相同则将值挂载到链表的最后。
remove时也是通过hashCode+equals的方法去判断删除对象的。
代码分析如下:
public class HashSetTest {
public static void main(String[] args) {
HashSet<Persons> persons = new HashSet<>();
Persons a = new Persons(1001,"aaa");
Persons b = new Persons(1002,"bbb");
persons.add(a);
persons.add(b);// a 和 b 是不同的对象,可以正常添加到HashSet集合中
a.name = "ccc";// 修改了a的name值
persons.remove(a);// 因为remove是通过hashCode和equals去找对象的,因为a对象的name值改变,因此hashCode会不一样,索引值也就改变了,他会找不到a对象,因此也无法删除a对象
System.out.println(persons);// 此处打印会将HashSet中的两个对象都打印出来
persons.add(new Persons(1001,"ccc"));// 此时,虽然新对象中的id和name值在HashSet中存在,但是通过此对象计算出的hashCode是和前一个不一样的,因此可以加入
System.out.println(persons);// 因此,此处有三个对象
persons.add(new Persons(1001,"aaa"));// 通过该对象计算出的hashCode得到的索引位置是有一个对象的,但是那个对象的name值已经改变,和该对象equals判断不为同一个对象,因此,可以加入,会加在那个对象的后面,形成链表
System.out.println(persons);// 因此,此时会打印四个对象
}
}
也许会有些地方不是很清楚,但是大家能读懂就好;有错误的地方,希望能够帮忙指正,谢谢!