修改ConcurrentHashMap的键为何会产生不同输出?附迭代示例
为什么在ConcurrentHashMap中修改键会导致输出结果不一致?
嘿,这个问题问到点子上了!咱们结合你给的代码和ConcurrentHashMap的核心特性一步步拆解:
先看你贴的迭代时新增元素的场景
你给出的代码里,在遍历keySet迭代器的过程中调用了map.put("SEVEN", 7),输出里出现了新增的SEVEN项,这其实是ConcurrentHashMap迭代器的**弱一致性(weakly consistent)**特性导致的:
- 和HashMap的“快速失败”迭代器不同,ConcurrentHashMap的迭代器不会在并发修改时抛出
ConcurrentModificationException - 迭代器创建时会记录当前Map的状态,但后续新增的元素可能被遍历到,也可能不会——这取决于新增元素所在的哈希桶是否已经被迭代过
- 你看到的输出里出现
SEVEN,就是因为这个新增元素的哈希桶在迭代器还没遍历到的时候就被添加完成,所以后续被纳入了迭代范围
重点:修改键对象本身才是导致输出异常的核心
你问题里提到的“修改键”,应该是指修改作为键的可变对象的属性,导致其hashCode()或equals()结果改变的情况(比如用自定义类当键,之后修改类里的字段),这才是会让输出结果彻底不符合预期的关键原因:
ConcurrentHashMap是基于哈希表实现的,键的哈希值直接决定了它在哈希桶中的存储位置:
- 当你把键存入Map时,会根据当时的哈希值计算位置并存放
- 如果之后修改了键的属性导致哈希值变化,会引发一系列问题:
- 调用
map.get(modifiedKey)时,会根据新哈希值查找错误的哈希桶,明明存过的键却返回null - 迭代器遍历时,可能会跳过这个键,或者在错误的位置读取到它,导致输出混乱
- 严重时还会破坏哈希表的结构,出现重复键、无法删除元素等问题
- 调用
举个直观的示例:
import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; // 自定义可变类作为键 class MutableKey { private String value; public MutableKey(String value) { this.value = value; } public void setValue(String value) { this.value = value; } @Override public int hashCode() { return value.hashCode(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MutableKey that = (MutableKey) o; return Objects.equals(value, that.value); } } public class Test { public static void main(String[] args) { ConcurrentHashMap<MutableKey, Integer> map = new ConcurrentHashMap<>(); MutableKey key = new MutableKey("OLD"); map.put(key, 1); // 修改键的属性,改变哈希值 key.setValue("NEW"); // 此时用修改后的键查找,输出null System.out.println(map.get(key)); // null // 但用旧属性的键却能找到对应值 System.out.println(map.get(new MutableKey("OLD"))); // 1 } }
这种情况本质是违反了哈希表的设计前提:键的hashCode和equals方法在存入Map后必须保持不变。
总结一下
- 迭代时新增元素导致输出出现新增项:是ConcurrentHashMap弱一致迭代器的正常特性
- 修改键对象本身(导致哈希/equals结果变化):属于破坏哈希表规则的操作,会引发查找失败、迭代结果混乱等问题,绝对要避免
内容的提问来源于stack exchange,提问作者PUNEET TIWARI
相关产品推荐
相关产品推荐

