You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中遍历HashMap问题:自定义类键无法正确获取

解决HashMap自定义类键遍历/匹配异常的问题

这问题我之前踩过好几次坑!核心原因其实是自定义类作为HashMap的键时,没有正确重写equals()和hashCode()方法,这也是HashMap键最容易踩的坑之一。

为什么基本类型/包装类没问题?

像Integer、String这类JDK自带的包装类/引用类型,已经按照业务逻辑正确实现了equals()和hashCode():

  • equals()是值比较:比如两个值为10的Integer对象,equals会返回true
  • hashCode()会基于对象的内容生成:比如Integer的hashCode就是它的数值本身

而你的自定义类默认继承自Object类,用的是父类的实现:

  • equals()默认是引用比较:只有两个对象是同一个内存地址时才返回true
  • hashCode()默认返回对象的内存地址哈希值

这就导致:即使你创建了两个内容完全相同的自定义类对象,HashMap也会把它们当成完全不同的键,遍历或获取时自然找不到预期的结果,看起来像“随机输出”。

解决步骤:重写equals()和hashCode()

必须为自定义键类重写这两个方法,并且要遵循以下约定:

  1. 如果两个对象通过equals()判断相等,它们的hashCode()必须返回相同的值
  2. 如果两个对象的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 13:42:32