Java中遍历HashMap问题:自定义类键无法正确获取
解决HashMap自定义类键遍历/匹配异常的问题
这问题我之前踩过好几次坑!核心原因其实是自定义类作为HashMap的键时,没有正确重写equals()和hashCode()方法,这也是HashMap键最容易踩的坑之一。
为什么基本类型/包装类没问题?
像Integer、String这类JDK自带的包装类/引用类型,已经按照业务逻辑正确实现了equals()和hashCode():
equals()是值比较:比如两个值为10的Integer对象,equals会返回truehashCode()会基于对象的内容生成:比如Integer的hashCode就是它的数值本身
而你的自定义类默认继承自Object类,用的是父类的实现:
equals()默认是引用比较:只有两个对象是同一个内存地址时才返回truehashCode()默认返回对象的内存地址哈希值
这就导致:即使你创建了两个内容完全相同的自定义类对象,HashMap也会把它们当成完全不同的键,遍历或获取时自然找不到预期的结果,看起来像“随机输出”。
解决步骤:重写equals()和hashCode()
必须为自定义键类重写这两个方法,并且要遵循以下约定:
- 如果两个对象通过
equals()判断相等,它们的hashCode()必须返回相同的值 - 如果两个对象的
hashCode()返回相同的值,它们的equals()不一定需要相等(但尽量减少这种情况,避免哈希冲突)
示例代码
假设你的自定义类是User,包含id和name字段,我们以id作为判断对象相等的核心依据:
// 错误示例:未重写equals和hashCode的自定义类 class BadUser { private Long id; private String name; public BadUser(Long id, String name) { this.id = id; this.name = name; } // 仅提供getter,无equals和hashCode } // 正确示例:重写了equals和hashCode的自定义类 class GoodUser { private Long id; private String name; public GoodUser(Long id, String name) { this.id = id; this.name = name; } // 重写equals:基于id判断对象相等 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GoodUser goodUser = (GoodUser) o; return id.equals(goodUser.id); } // 重写hashCode:基于id生成哈希值 @Override public int hashCode() { return id.hashCode(); } // 可选:重写toString方便调试 @Override public String toString() { return "User{id=" + id + ", name='" + name + "'}"; } }
测试验证
public class HashMapTest { public static void main(String[] args) { // 测试错误的自定义类 Map<BadUser, String> badMap = new HashMap<>(); BadUser user1 = new BadUser(1L, "Alice"); BadUser user2 = new BadUser(1L, "Alice"); badMap.put(user1, "Admin"); // 这里会返回null,因为user1和user2被认为是不同的键 System.out.println(badMap.get(user2)); // 测试正确的自定义类 Map<GoodUser, String> goodMap = new HashMap<>(); GoodUser user3 = new GoodUser(1L, "Alice"); GoodUser user4 = new GoodUser(1L, "Alice"); goodMap.put(user3, "Admin"); // 这里会返回"Admin",因为equals和hashCode正确匹配 System.out.println(goodMap.get(user4)); // 遍历也能正常获取所有键值对 for (Map.Entry<GoodUser, String> entry : goodMap.entrySet()) { System.out.println(entry.getKey() + " -> " + entry.getValue()); } } }
额外小技巧
- Java 7及以上可以用
java.util.Objects工具类简化重写,避免手动写判断出错:@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GoodUser goodUser = (GoodUser) o; return Objects.equals(id, goodUser.id) && Objects.equals(name, goodUser.name); } @Override public int hashCode() { return Objects.hash(id, name); } - Java 16及以上可以直接用
record类,它会自动生成正确的equals()、hashCode()和toString():record User(Long id, String name) {}
按照这个方法改完,你再遍历HashMap的时候,就能正确识别自定义键了,不会再出现莫名其妙的问题~
内容的提问来源于stack exchange,提问作者mr tech zubair
相关产品推荐
相关产品推荐

