如何让Java的Set判定两个name相同的Monkey对象为同一对象?
解决HashSet无法识别name相同的Monkey对象问题
这个问题其实是Java里关于HashSet工作机制的经典坑——只重写equals不重写hashCode肯定会出问题!
为什么当前代码会失效?
HashSet判断元素是否重复的逻辑是先比hashCode,再比equals:
- 默认情况下,
Object类的hashCode是基于对象内存地址生成的,所以你new出来的monkey1和monkey2哪怕name完全一样,它们的hashCode也是不同的。 - 当
HashSet看到两个对象hashCode不同时,会直接把它们放到不同的"桶"里,根本不会去调用你重写的equals方法做进一步比较。这就是为什么明明name相同,contains(monkey2)返回false,最终集合size是2。
正确的解决方案:同时重写equals和hashCode
根据Java的规范:如果两个对象通过equals方法判断为相等,那么它们的hashCode必须相等。反之,hashCode相等的对象不一定equals相等,但equals相等的对象hashCode必须一致。
我们需要修改Monkey类,同时重写这两个方法,并且让hashCode的生成依赖于name属性:
import java.util.Objects; private class Monkey{ private final String name; // 建议设为final,避免后续修改name导致HashSet失效 public Monkey(String name) { this.name = name; } @Override public boolean equals(Object obj) { if (this == obj) return true; // 引用相同直接返回true,优化性能 if (obj == null || getClass() != obj.getClass()) return false; // 判空+校验类型 Monkey monkey = (Monkey) obj; return Objects.equals(name, monkey.name); // 用Objects.equals避免name为null时的空指针 } @Override public int hashCode() { return Objects.hash(name); // 基于name生成hashCode,name相同则hashCode一致 } } @Test public void contain(){ Monkey monkey1 = new Monkey("Jon"); Monkey monkey2 = new Monkey("Jon"); java.util.Set<Monkey> monkeySet = new HashSet<>(); monkeySet.add(monkey1); System.out.println(monkeySet.contains(monkey2)); // 现在输出true monkeySet.add(monkey2); System.out.println(monkeySet.size()); // 现在输出1 }
额外注意事项
- 尽量把参与equals和hashCode计算的字段设为
final,如果后续修改name的值,会导致该对象在HashSet中的位置失效,无法被正确查找或删除。 - 重写
equals时,一定要做类型校验(getClass() != obj.getClass()),避免和子类对象错误匹配。 - 使用
Objects.equals和Objects.hash工具类,可以简化代码同时避免空指针异常。
内容的提问来源于stack exchange,提问作者Shuai Li
相关产品推荐
相关产品推荐

