引言:HashMap——朋友还是敌人?
每个Java开发者迟早都会遇到HashMap。这可能是Java中Map接口最流行的实现。但矛盾的是:所有人都知道如何向映射中添加元素,但一旦涉及到遍历(迭代),真正的混乱就开始了。Stack Overflow上“Iterate through a HashMap”的问题获得了数千次浏览,而实际项目中的错误数量更是惊人。
在本文中,我们将分析5个最危险的错误,这些错误在Java中遍历HashMap时可能导致应用程序崩溃、数据丢失和代码难以阅读。您不仅会了解错误的方式,还会获得每种情况的可行模板。开始吧!
错误#1:ConcurrentModificationException——“边遍历边删除”
问题是什么?
最经典的错误。您正在遍历HashMap并尝试通过map.remove(key)删除元素。在90%的情况下,这会导致ConcurrentModificationException。
// ❌ 危险:会抛出异常!HashMap<String, Integer> scores = new HashMap<>();scores.put("Alice", 90);scores.put("Bob", 50);scores.put("Charlie", 70);
for (String name : scores.keySet()) { if (scores.get(name) < 60) { scores.remove(name); // ConcurrentModificationException! }}为什么会这样?
HashMap的内部迭代器维护一个修改计数器(modCount)。当您直接通过集合删除元素时,计数器会改变,但迭代器并不知道。在下一次迭代时,它会检查:“嘿,计数器不匹配!抛出异常。”
如何修复?
使用Iterator及其remove()方法:
// ✅ 正确:使用IteratorHashMap<String, Integer> scores = new HashMap<>();scores.put("Alice", 90);scores.put("Bob", 50);scores.put("Charlie", 70);
Iterator<Map.Entry<String, Integer>> iterator = scores.entrySet().iterator();while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); if (entry.getValue() < 60) { iterator.remove(); // 安全删除! }}从Java 8开始,还有一种更优雅的方式——removeIf():
// ✅ 更好:removeIf (Java 8+)scores.entrySet().removeIf(entry -> entry.getValue() < 60);错误#2:通过keySet()和get()进行慢速遍历
问题是什么?
许多新手会这样做:
// ❌ 慢速:keySet() + get() — 双重查找for (String key : map.keySet()) { System.out.println("Key: " + key + ", Value: " + map.get(key));}乍一看一切正常。但问题在于,map.get(key)内部会再次查找哈希并遍历桶。每次循环迭代时,您都会对HashMap进行两次调用,而不是一次。对于小型映射来说这并不明显,但当有数十万条记录时,性能损失可达30-40%。
如何修复?
始终使用entrySet()——它一次遍历返回“键-值”对:
// ✅ 快速:entrySet() — 单次遍历for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());}或者使用Java 8的forEach:
// ✅ 简洁:forEach (Java 8+)map.forEach((key, value) -> { System.out.println("Key: " + key + ", Value: " + value);});错误#3:元素顺序混淆
问题是什么?
您编写了遍历HashMap的代码,并期望元素按插入顺序排列。但得到的却是完全不同的顺序。这会导致错误,这些错误