为何HashSet可插入两个相等的Quadruple实例?技术咨询
问题原因分析
这个问题我太熟了——本质是你没重写Quadruple类的equals()和hashCode()方法,导致HashSet没法正确判断两个实例是不是“相同”的元素。
HashSet 判断元素重复的核心逻辑是先比较哈希码是否相等,再用equals方法做最终确认,而你的Quadruple类直接继承了Object类的默认实现:
Object.equals():默认比较对象的内存地址,只有两个引用指向同一个对象时才返回trueObject.hashCode():默认返回和对象内存地址绑定的数值,哪怕两个实例属性完全一致,只要是不同对象,哈希码大概率不同
所以哪怕两个Quadruple的a/b/c/d属性完全一样,在HashSet眼里它们也是两个独立的对象,自然能被重复插入。
解决方法:重写equals()和hashCode()
要让HashSet正确识别属性相同的Quadruple实例,你必须手动重写这两个方法,并且严格遵守equals相等的对象,hashCode必须相等的约定。
因为你的histogram是由a/b/c/d派生出来的,所以只需要基于这四个int属性实现即可(如果要包含histogram,注意HashMap.equals()会比较所有键值对,这里因为histogram由四个属性生成,结果和用a/b/c/d判断是一致的)。
示例代码:
public static class Quadruple { int a; int b; int c; int d; Map<Integer, Integer> histogram; public Quadruple(int a, int b, int c, int d) { this.a = a; this.b = b; this.c = c; this.d = d; this.histogram = new HashMap<>(); // 简化histogram初始化逻辑 histogram.put(a, histogram.getOrDefault(a, 0) + 1); histogram.put(b, histogram.getOrDefault(b, 0) + 1); histogram.put(c, histogram.getOrDefault(c, 0) + 1); histogram.put(d, histogram.getOrDefault(d, 0) + 1); } // 重写equals方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Quadruple quadruple = (Quadruple) o; return a == quadruple.a && b == quadruple.b && c == quadruple.c && d == quadruple.d; } // 重写hashCode方法 @Override public int hashCode() { return Objects.hash(a, b, c, d); } }
补充提醒
- 千万不要只重写其中一个方法:如果只重写
equals()不重写hashCode(),会导致两个equals相等的对象哈希码不同,HashSet会把它们放到不同的桶里,永远不会触发equals比较,还是会被当成不同元素插入。 - 可以直接用IDE的自动生成功能(比如IntelliJ的
Alt+Insert)来生成这两个方法,避免手动编写出错。
内容的提问来源于stack exchange,提问作者Alek
相关产品推荐
相关产品推荐

