如何改造User POJO使其作为HashMap键并正确重写equals/hashCode
核心逻辑前提
HashMap判断两个键是否重复的逻辑是固定的:
- 第一步调用键的
hashCode()方法计算哈希值,定位到对应的存储桶位置 - 第二步遍历桶内的节点,调用键的
equals()方法和已有键做比对,只要存在返回true的节点,就判定为重复键
你当前的需求明确了相等规则:两个User实例只要userId属性值相同,就算同一个对象,所有改造都必须严格对齐这个规则,equals()和hashCode()两个方法缺一不可。
具体改造步骤
- 补全类的初始化能力:原User类只有getter方法,没法给属性赋值,可以加全参构造方便实例化;如果要保证HashMap键的稳定性(避免属性被修改后哈希值变化,导致找不到对应存储的值),不要给
userId加setter方法,username可以按需加setter。 - 重写
equals()方法:判定逻辑按顺序走:先判断是不是同一个对象引用(是则直接返回true),再判断入参是否为空、是否和当前对象是同一个类型(不满足直接返回false),最后强转入参为User类型,比对两个实例的userId值即可,比对时用java.util.Objects.equals()避免空指针。 - 重写
hashCode()方法:只基于userId属性计算哈希值,绝对不能把username纳入计算逻辑,否则会出现userId相同的两个实例哈希值不一致,HashMap识别为不同键的问题。 - (可选)如果项目用了Lombok,不用手写两个方法,给类加
@EqualsAndHashCode(onlyExplicitlyIncluded = true)注解,再给userId字段加@EqualsAndHashCode.Include即可,注意不要给username加这个注解。
改造后完整代码
import java.util.Objects; public class User { private Long userId; private String username; // 全参构造用于实例化 public User(Long userId, String username) { this.userId = userId; this.username = username; } public Long getUserId() { return userId; } public String getUsername() { return username; } // 仅提供username的setter,避免userId被修改导致哈希值变动 public void setUsername(String username) { this.username = username; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; // 只比对userId,符合需求的相等规则 return Objects.equals(userId, user.userId); } @Override public int hashCode() { // 只基于userId计算哈希 return Objects.hashCode(userId); } }
常见避坑点
这几个错误是这类场景最容易踩的:
- 只重写
equals()不重写hashCode():Object类默认的hashCode()是基于对象内存地址生成的,两个userId相同的不同User实例,哈希值大概率不一样,HashMap会直接把它们当成两个完全无关的键存储,根本走不到equals比对的逻辑。- 把
username放到equals()或hashCode()的判定逻辑里:只要修改了实例的username,哈希值和equals结果就会变化,之前存在HashMap里的对应键值对就再也匹配不到了。- 给
userId加setter方法:实例存入HashMap之后如果修改了userId值,同样会导致哈希值变化,直接丢失原有的键值映射。
内容的提问来源于stack exchange,提问作者Criwran
相关产品推荐
相关产品推荐

