前面一篇,我们介绍了利用HashSet存储自定义对象的保证元素唯一性的优化过程。这篇,我们来阅读和解释下使用IDE快速生成重写equals和hashCode方法的代码。然后总结下HashSet保证存储元素唯一性的原理。
1.快速生成的equals和hashCode重写代码
在IDE工具,我们可以使用alt+shift+s,然后点击h,可以快速生成自定义对象重写Object类的equals和hashCode方法。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
2.解释下hashCode中常量为什么是31这个数
上面代码使用的prime=31,为什么需要使用31,而不是随便一个数字呢?主要有下面三个原因。
1)31是一个质数,只能被1和自己本身整除
2)31是一个不大也不小的质数
3)31是2的5次方减去1,利用位移操作能够快速得到结果,方便计算。
质数可以减少公约数的个数,降低通过计算之后两个对象的哈希值还相等的概率。31作为一个不大,也不小的数,如果太大的质数,计算之后的结果可能大于int类型的最大表示范围。我们知道,计算机里面位移操作的计算速度是最快的。
3.给equals方法添加代码注释
@Override
public boolean equals(Object obj) {
if (this == obj) //调用的对象和传入的对象是同一个对象
return true; //直接返回true
if (obj == null) //传入对象为null
return false; //返回false
if (getClass() != obj.getClass()) //判断两个对象对应的字节码文件是否是同一个字节码(Java中内存中不可能存在两个相同的字节码文件)
return false; //如果不是直接返回false
Person other = (Person) obj; //上面的可能都排除,只能是符合类型传入,做强制转换,向下转型
if (age != other.age) //调用对象的年龄不等于传入对象的年龄
return false; //返回false
if (name == null) { //调用对象的姓名为null
if (other.name != null) //调用对象的姓名为null
return false; //返回false
} else if (!name.equals(other.name))//调用对象的姓名不等于传入对象的姓名
return false; //返回false
return true; //前面判断都经历了,返回true
}
4.HashSet原理
我们使用Set集合都是需要去掉重复元素的,如果在存储的时候逐个equals()比较,效率比较低,哈希算法提高了去重复的效率,降低了使用equals()方法的次数。
当HashSet调用add()方法存储对象的时候,先调用对象的hashCode()方法得到一个哈希值,然后在集合中查找是否有哈希值相同的的对象,如果没有哈希值相同的对象就直接存入集合;如果有哈希值相同的对象,就和哈希值相同的对象逐个进行equals()方法比较,比较结果为false就存入集合,true就不存入集合。
5.自定义对象存储HashSet去重的步骤
1)类中必须重写hashCode()和equals()方法
2)hashCode():属性相同的对象返回值必须相同,属性不同的返回值尽量不同(提高效率)
3)equals():属性相同返回true,属性不同返回false,返回false的时候存储到集合