在Java中优雅删除HashMap元素需特别注意遍历时的并发修改异常。以下是5种实现方式及最佳实践:
- 增强for循环
HashMap<String, String> map = new HashMap<String, String>() {{
put("1", "1");
put("2", "2");
put("3", "3");
put("4", "4");
put("5", "5");
}};
//1. 增强for
CopyOnWriteArraySet<Map.Entry<String, String>> entries = new CopyOnWriteArraySet<>(map.entrySet());
for (Map.Entry<String, String> entry : entries) {
if (StringUtils.equals(entry.getKey(), "1")){
map.remove(entry.getKey());
}
}
System.out.println(map);
- 迭代器删除
ConcurrentHashMap<String, String> iteratorConcurrentHashMap = new ConcurrentHashMap<>();
Iterator<Map.Entry<String, String>> iterator = new ConcurrentHashMap<>(map).entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<String, String> next = iterator.next();
if (StringUtils.equals(next.getKey(), "3")){
iterator.remove();
}else {
iteratorConcurrentHashMap.put(next.getKey(), next.getValue());
}
}
System.out.println(iteratorConcurrentHashMap);
- foreach+条件判断
ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>(map);
concurrentHashMap.forEach((k, v) -> {
if (StringUtils.equals(k, "2")){
map.remove(k);
}
});
System.out.println(map);
- Stream流处理
Map<String, String> streamMap = map.entrySet().stream().filter(entry -> StringUtils.equals(entry.getKey(), "5")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
System.out.println(streamMap);
- 使用removeIf(推荐使用)
ConcurrentHashMap<String, String> removeIfConHashMap = new ConcurrentHashMap<>(map);
removeIfConHashMap.entrySet().removeIf(entry -> StringUtils.equals(entry.getKey(), "4"));
System.out.println(map);
推荐优先级排序
-
removeIf + ConcurrentHashMap (最优解)
map.entrySet().removeIf(entry -> 条件表达式);
- 线程安全:直接利用
ConcurrentHashMap
的原子性删除 - 性能最佳:内部通过分段锁实现高效批量删除
- 代码简洁:函数式编程风格,可读性高
- 线程安全:直接利用
-
迭代器 + ConcurrentHashMap
Iterator<Map.Entry<K,V>> it = map.entrySet().iterator(); while(it.hasNext()) { if(条件判断) { it.remove(); // 实际调用ConcurrentHashMap的原子删除 } }
- 安全可靠:迭代器与容器状态一致性有保障
- 细粒度控制:适合复杂删除逻辑
-
foreach + ConcurrentHashMap
for(Map.Entry<K,V> entry : map.entrySet()) { if(条件判断) { map.remove(entry.getKey()); // 依赖ConcurrentHashMap的线程安全保证 } }
- 弱一致性风险:遍历期间可能漏删新增元素
- 次优选择:适合简单场景,需注意逻辑完备性
不推荐方案
-
增强for+CopyOnWriteArraySet
CopyOnWriteArraySet<K> keySet = new CopyOnWriteArraySet<>(map.keySet()); for(K key : keySet) { if(条件判断) map.remove(key); }
- 性能陷阱:每次修改触发全量复制,时间复杂度 O ( n 2 ) O(n^2) O(n2)
- 数据不一致:快照与实时状态可能存在差异
-
Stream流+ConcurrentHashMap
ConcurrentHashMap<K,V> newMap = map.entrySet().stream() .filter(entry -> !删除条件) .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
- 空间浪费:需创建新容器,内存开销翻倍
- 非原子操作:生成新容器期间可能丢失并发更新
关键指标对比
方案 | 时间复杂度 | 内存消耗 | 代码简洁度 |
---|---|---|---|
removeIf | O ( n ) O(n) O(n) | 低 | ★★★★★ |
迭代器 | O ( n ) O(n) O(n) | 低 | ★★★☆☆ |
foreach | O ( n ) O(n) O(n) | 低 | ★★★★☆ |
CopyOnWriteArraySet | O ( n 2 ) O(n^2) O(n2) | 高 | ★★☆☆☆ |
Stream流 | O ( n ) O(n) O(n) | 高 | ★★★☆☆ |
最终建议
- 并发场景:优先使用
removeIf
,次选显式迭代器 - 简单逻辑:可用
foreach
简化代码 - 禁止使用:会产生性能瓶颈或数据不一致的方案
(注:所有方案均假设使用ConcurrentHashMap
替代非线程安全的HashMap
)