场景引入
在代码的虚拟世界里,有一个名叫小夏的程序员,他正在开发一款图书管理系统。其中,Book类用来描述每一本图书,包含书名、作者和 ISBN 号等属性。小夏满心欢喜地将图书信息录入系统,却在使用HashSet存储图书时,发现了一个奇怪的现象 —— 明明是两本内容完全相同的《Java 编程思想》,系统却把它们当成了不同的书。
这个 “乌龙事件” 的背后,藏着equals和hashCode这两个 “身份验证官” 的秘密。在 Java 世界中,Object类是所有类的老祖宗,它提供的equals方法默认比较的是对象的地址,就像在问 “这两个东西是不是同一个”。小夏创建的两个Book对象,虽然内容一模一样,但在内存中占据不同的位置,就像两本摆在不同书架上的同款图书,Object的equals方法自然判定它们 “不相等”。而hashCode方法同样继承自Object,默认对比的也是对象的引用地址,这就好比给每本书都贴上了一个独一无二的 “位置标签”,不同书架上的书,标签自然不同。
这样的判断逻辑,在很多实际场景中并不适用。小夏希望系统能像图书馆管理员一样,通过书名、作者等内容来判断两本书是否相同,而不是纠结它们放在哪个位置。这就需要重写equals和hashCode方法,让它们按照 “内容是否一致” 来做判断。
以HashSet集合为例,它的去重机制就依赖这两位 “身份验证官” 的合作。当小夏向HashSet中添加两本相同的书时,如果没有重写方法,HashSet先调用默认的hashCode方法,由于两个Book对象地址不同,hashCode返回的哈希值也不同,就像两本书的 “位置标签” 不一样。HashSet一看标签不同,直接认定是两本书,连equals方法都懒得调用,结果两本相同的书都被添加进了集合,这就是小夏遇到问题的原因。
但如果小夏重写了equals和hashCode方法,情况就大不相同了。重写后的hashCode方法会根据图书的属性值计算哈希值,就像根据书名、作者等信息生成一个 “内容标签”。当两本相同的书生成的 “内容标签” 一样时,HashSet会继续调用equals方法进行细致比对。确认两本书内容确实相同后,HashSet就会判定它们是同一本书,只保留一本,完美实现了去重功能。
另一个有趣的例子是String类的intern方法。在字符串的世界里,有一个神秘的 “字符串常量池”。当小夏创建一个字符串String str1 = "Hello"时,系统会先去常量池里找找有没有相同内容的字符串。如果找到了(通过equals方法判断内容相同),就直接返回常量池中的字符串引用,就像从仓库里取出已有的货物。如果没找到,才会在常量池中新增这个字符串,并返回它的引用,这就好比生产了一件新商品放入仓库。equals和hashCode在这里同样发挥着重要作用,它们确保了字符串在常量池中的唯一性和高效管理。
经过这次经历,小夏明白了,在 Java 编程的世界里,合理重写equals和hashCode方法,就像给对象赋予了准确的 “身份认证”,让程序能够按照我们的预期运行,避免那些令人困惑的 “乌龙事件”。这两个方法看似简单,却在数据处理和对象比较中扮演着至关重要的角色,是每个 Java 开发者都必须掌握的 “通关秘籍”。
博主总结
- 重写equal原因:在大多数情况来说,equals 的判断是没有什么意义的!因为它是比较地址。例如,使用 Object 中的 equals 比较两个自定义的对象是否相等,这就完全没有意义(因为无论对象是否相等,结果都是 false)。
- 重写hashcode原因:没有重写 hashCode 方法,会直接执行 Object 中的 hashCode 方法,而 Object 中的 hashCode 方法对比的是引用地址。重写了 hashCode 方法,会执行重写的 hashCode 方法,此时对比的是两个对象的所有属性值。
- 一句话来说,就是在生产环境中我们所说的比较对象说的是比较值,而在不重写的情况下比较的是对象地址,而正常情况下比较地址基本都是false。
- 举个例子来说。hashSet集合。
- hashSet的去重就是依赖于这两个方法,首先执行hashCode方法再执行equal方法
- 那么默认情况下,Set 进行去重操作时,会先判断两个对象的 hashCode 是否相同,此时因为没有重写 hashCode 方法,所以会直接执行 Object 中的 hashCode 方法,而 Object 中的 hashCode 方法对比的是两个不同引用地址的对象,所以结果是 false,那么 equals 方法就不用执行了,直接返回的结果就是 false:两个对象不是相等的,于是就在 Set 集合中插入了两个相同的对象。
- 但是,如果在重写 equals 方法时,也重写了 hashCode 方法,那么在执行判断时会去执行重写的 hashCode 方法,此时对比的是两个对象的所有属性的 hashCode 是否相同,于是调用 hashCode 返回的结果就是 true,再去调用 equals 方法,发现两个对象确实是相等的,于是就返回 true 了,因此 Set 集合就不会存储两个一模一样的数据了,于是整个程序的执行就正常了。
- hashSet的去重就是依赖于这两个方法,首先执行hashCode方法再执行equal方法
- 再举个例子。Intern方法。
- 如果当前字符串内容存在于字符串常量池(即 equals()方法为 true,也就是内容一样),直接返回字符串常量池中的字符串。
- 否则,将此 String 对象添加到池中,并返回 String 对象的引用。