为何HashSet未维护唯一性?修改Employer对象ID后集合大小未变的原因
为什么修改HashSet中对象的ID后集合大小仍为2?是否破坏了不变性?
这是个非常经典的HashSet/HashMap使用误区,咱们一步步拆解原因:
1. 为什么集合大小还是2?
HashSet底层完全依赖HashMap实现元素唯一性,当你把对象加入集合时,会按以下逻辑执行:
- 先计算对象的
hashCode,以此确定它要存入HashMap的哪个桶(bucket) - 然后在该桶内用
equals方法检查是否已有相同元素,没有的话就完成存入
回到你的场景:
- 加入
employer1(id=10)时,基于id=10算出对应的hashCode,存入了某个桶 - 加入
employer2(id=11)时,基于id=11算出另一个hashCode,存入了另一个桶 - 当你修改
employer2.setId(10L)后,employer2的hashCode确实变成了和employer1一样的值,但**HashMap不会自动更新它的桶位置**——集合不会监听元素的字段变化来重新分配存储位置。
现在HashMap里的状态是:
employer1待在id=10对应的桶里employer2还待在原来id=11对应的桶里,但它的hashCode已经变成了id=10的结果
此时如果检查集合是否存在重复,HashSet会用employer2新的hashCode去找对应桶,但employer2根本不在这个桶里,自然不会被判定为和employer1重复,集合大小也就保持为2。
2. 这确实破坏了集合的核心不变性
HashSet/HashMap有个必须遵守的核心约定(也是Java集合框架的关键不变性规则):
作为集合key的对象,在存入后绝对不能修改任何会影响
equals或hashCode计算的字段
你的Employer类中,equals和hashCode完全依赖id字段,修改id直接改变了这两个方法的返回结果,这就彻底违反了约定,会引发一系列问题:
- 无法正确查找元素:比如调用
hashSet.contains(employer2)会返回false,即使它明明在集合里 - 无法正确删除元素:调用
hashSet.remove(employer2)会找不到目标元素 - 集合出现逻辑重复:就像现在的情况,两个
equals判定为相等的对象同时存在,完全违背了HashSet“存储不重复元素”的设计初衷
如何避免这种问题?
- 优先把作为集合key的对象设计成不可变类:比如把
id设为final,只在构造方法中赋值,不提供setter方法 - 如果必须修改对象字段,一定要先把对象从集合中移除,修改完成后再重新加入集合
内容的提问来源于stack exchange,提问作者Farhan stands with Palestine
相关产品推荐
相关产品推荐

