Java中Object类默认hashCode()返回内存地址,导致未重写该方法的自定义类作为HashMap键时,内容相同的对象因哈希值不同被散列到不同桶中,无法正确查找或覆盖,违背业务预期。

Java 中 Object 类默认的 hashCode() 返回的是对象在 JVM 中的内存地址(一个 32 位整数),这个值对每个对象实例都是唯一的,哪怕两个对象内容完全相同。当自定义类作为 HashMap 的 key 且未重写 hashCode() 和 equals() 时,HashMap 就会按这套“地址唯一性”逻辑工作——结果是:逻辑上相等的对象,被当成完全不同的键处理。
HashMap 查找依赖两步校验
HashMap 不是直接比内容,而是分两阶段判断键是否匹配:
- 先用
key.hashCode()计算哈希值,再对数组长度取模,定位到某个桶(bucket)位置; - 若该桶非空,则遍历其中的链表或红黑树节点,用
key.equals(目标key)逐个比较,确认是否真为同一个键。
如果没重写 hashCode(),哪怕两个 Student 对象姓名、学号都一样,它们的哈希值也不同 → 被散列到不同桶里 → 第二步根本不会执行,get() 直接返回 null。
重复插入导致“假重复”
假设你连续两次 put(new Student("张三", 20), "A级"):
立即学习“Java免费学习笔记(深入)”;
- 两次 new 出来的 Student 实例内存地址不同 →
hashCode()不同 → 可能落在不同数组索引上; - HashMap 认为这是两个不同的 key,于是都存入,而不是覆盖;
- 后续用另一个新
new Student("张三", 20)去get(),因哈希值不匹配,连桶都进不去,查不到值。
String 为什么不用重写?
String 是特例:它已重写了 hashCode() 和 equals(),规则是“内容相同则哈希值相同,且 equals() 返回 true”。所以 map.put("abc", 1); map.get("abc") 总能命中。而你的自定义类没有这层保障,JVM 不会自动帮你做语义等价判断。
关键不是“能不能运行”,而是“行为不可预期”
未重写时代码能编译、能运行,但表现违背业务直觉:
- 相同含义的对象无法互相 get/contains;
- 同一对象多次 put 会被当作多个键;
- 放进 HashSet 的自定义对象,可能重复添加;
- 一旦对象字段参与 equals 判断却没同步进 hashCode 计算,就会彻底失效。
这不是 bug,是契约未遵守——HashMap 的设计严格依赖 equals() 和 hashCode() 的一致性约定。


















