equals与hashCode重写
在某些场景,需要重写某个对象的equals()方法来满足我们的需求,重写equals()方法时一定注意要重写hashCode()方法。为什么重写equals()方法就一定要重写hashCode()方法呢?
例如,现在统计不同种类的书的数量,书分类的规则是:书名与作者一样这些书就是相同的。首先定义Book对象,添加相应字段以及set、get方法。
1 | public class Book{ |
- 根据业务需求重写equals()方法,如下所示
1 |
|
首先编写测试代码测试equals是否能满足我们的需求:
1 | public static void main(String[] args){ |
输出结果为:
1 | "D:\Program Files\Java\jdk1.8.0_91\bin\java" ... |
可以看到,重写equals已满足我们最开始的需求,当书名与作者相同时,认为这两本书相同。为什么还要重写hashCode()方法呢?其实很多地方建议甚至强制要求这样写,是因为equals()重写的对象存入依赖hashCode()方法的容器类时就会发生问题,例如HashMap、HashSet等容器,很多方法都依赖对象的hash值。
- 接下来验证不重写hashCode()方法会产生什么后果,是否能正常使用。我们将Book对象的实例存入HashSet容器中,验证HashSet去重是否能够成功。
1 | Book mathThree = new Book("math", "Dave"); |
输出结果为:
1 | "D:\Program Files\Java\jdk1.8.0_91\bin\java" ... |
由上可发现,两次add均成功,并没有将两个书名、作者相同的Book对象实例当做相等来处理。Set内部添加了两个Book对象。查看HashSet的源码,追查原因:
1 | public V put(K key, V value) { |
通过查看16行与30行代码发现,判断两个元素相等首先判断他们的hash值是否相等,而我们并没有重写hash值,故HashSet认为mathThree、mathFour这两个实例对象是不相同的,故HashSet会将两个相等的元素存进去。查看HashMap的源码也是如此,在判断两个Key元素是否相等时,首先判断Key的hash值是否相同,若hash不同则认为Key元素不相等。
- 接下来我们重写Book对象的hashCode()方法,再次验证
1 |
|
输出结果为
1 | "D:\Program Files\Java\jdk1.8.0_91\bin\java" ... |
可以发现,重写hashCode()之后HashSet添加成功。HashSet将mathThree、mathFour作为“相等”的两个实例对象,故只添加了一个。
同样的我们如果只重写hashCode()而不重写equals()方法,因为无法满足业务上的需求:书名与作者相同就是同一本书。
- 总结:在重写equals()方法时,必须重写hashCode()方法,否则对象将无法配合HashSet、HashMap、LinkedHashMap、TreeMap等容器正常使用。故强烈建议两个方法同时重写。