HashMap TreeNode putTreeVal方法中哈希相等但对象不等的场景疑问
哈希相等但==、equals均为false的触发场景解析
这个问题问得特别好!咱们常说的「重写equals必须重写hashCode」是Java的最佳实践规范,但现实中不管是代码不规范,还是哈希算法的天然特性,都可能触发你提到的这个分支。下面具体拆解几种场景:
1. 只重写hashCode但未重写equals的不规范代码
这是最常见的触发场景。Object类的默认equals是比较对象引用(和逻辑一致),如果自定义类只重写了hashCode,让不同对象返回相同的哈希值,但没重写equals,就会出现:哈希值相同,但和equals都返回false。
举个直观的例子:
class BadPracticeClass { private int num; public BadPracticeClass(int num) { this.num = num; } // 强制让所有对象返回相同哈希值 @Override public int hashCode() { return 42; } // 未重写equals,沿用Object的引用比较逻辑 } // 测试代码 BadPracticeClass obj1 = new BadPracticeClass(1); BadPracticeClass obj2 = new BadPracticeClass(2); System.out.println(obj1.hashCode() == obj2.hashCode()); // true System.out.println(obj1 == obj2); // false System.out.println(obj1.equals(obj2)); // false
此时就会进入HashMap红黑树的目标分支。
2. equals和hashCode逻辑不一致的不规范代码
有些开发者虽然同时重写了两个方法,但逻辑不匹配:比如hashCode基于字段A计算,equals却只比较字段B,导致不同对象哈希值相同,但equals不认为它们相等。
例子:
class InconsistentClass { private String name; private int age; public InconsistentClass(String name, int age) { this.name = name; this.age = age; } @Override public int hashCode() { // 哈希值仅由name决定 return name.hashCode(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; InconsistentClass that = (InconsistentClass) o; // equals仅比较age,和hashCode的计算字段无关 return age == that.age; } }
如果两个对象的name哈希值相同,但age不同(比如name是"Alice"和"Bob"刚好哈希碰撞,age分别是20和30),就会出现哈希相等,但==、equals都为false的情况。
3. 遵守规范但天然发生哈希碰撞
即使严格按照规范重写equals和hashCode(规范要求:equals相等时hashCode必须相等,但hashCode相等时equals可以不相等),不同对象也可能天然出现哈希值相同的情况(哈希碰撞)。
比如Java字符串的哈希算法就存在已知的碰撞对:
String str1 = "FB"; String str2 = "Ea"; System.out.println(str1.hashCode()); // 输出2236 System.out.println(str2.hashCode()); // 输出2236 System.out.println(str1.equals(str2)); // false System.out.println(str1 == str2); // false
这种完全符合规范的场景,也会触发HashMap红黑树的目标分支。
为什么HashMap要处理这种场景?
当出现哈希相等但对象不相等的情况时,HashMap需要继续在红黑树中确定节点的位置:
- 先尝试用Comparable接口的compareTo方法比较(如果key实现了该接口)
- 如果没有实现Comparable或者比较结果为0,就递归查找左右子树
- 最后如果还是无法区分,就用
tieBreakOrder方法(基于对象的System.identityHashCode)来决定节点方向,保证红黑树能正确插入和查找。
内容的提问来源于stack exchange,提问作者shengbang he
相关产品推荐
相关产品推荐

