高效指南:怎么遍历哈希表中的元素?几种方法优劣大PK!

哈希表(HashMap),这玩意儿,咱们程序员天天跟它打交道。它就像一个巨大又有点乱的抽屉,你往里塞了一堆东西,每个东西都贴了个标签(这就是Key-Value嘛),现在你想把抽屉里的东西都翻出来瞅瞅,或者找点啥,该怎么办?这就是我们今天唠的嗑:怎么遍历哈希表中的元素

别看这问题基础,里头的门道可不少。用对了方法,代码优雅,效率飞起;用错了,嘿,那可能就是一场小小的性能灾难,或者在某个深夜给你埋个大坑。

王道之选:entrySet(),没得跑!

咱们开门见山,先说结论:在绝大多数情况下,如果你既需要Key也需要Value,用 entrySet() 是最优解。没有之一。

为啥我敢这么说?

你想想,entrySet() 这哥们儿,它返回的是一个 Set<Map.Entry<K, V>>Map.Entry 是个啥?它就是 Key-Value 这对儿小情侣的“合体”。你遍历这个Set,每一次迭代拿到的 entry 对象,身上就同时带着 getKey()getValue() 两个方法。

java
// 伪代码示意
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
// 拿着key和value,你想干嘛就干嘛
System.out.println("钥匙:" + key + ",宝贝:" + value);
}

看到了吗?一步到位,直接把钥匙(Key)和里面的宝贝(Value)一套给你。效率最高,因为哈希表的内部实现就是这么存的,你这么取,完全是顺着它的毛摸,它能不舒服、不快吗?

信我,用 entrySet(),准没错。把它刻在你的DNA里。

先拿钥匙再开锁:keySet(),偶尔为之

当然,还有一种很常见的写法,就是用 keySet()。这个方法,顾名思义,就是先把所有Key打包成一个Set给你。然后呢?你再拿着这个Set去遍历。

java
// 伪代码示意
for (String key : map.keySet()) {
Integer value = map.get(key); // 问题就出在这儿!
System.out.println("钥匙:" + key + ",宝贝:" + value);
}

看出问题了吗?这操作,就像你先拿到了一串储物柜的号码牌,然后每拿到一个号码牌,你都得重新跑回总服务台,用这个号码去查询柜子里放了什么。你每循环一次,就要做一次 map.get(key) 操作。这本质上又是一次哈希计算和查找。

如果你的HashMap巨大无比,这来来回回的查询,累积起来的开销可就不容小觑了。相比 entrySet() 的一次性打包,keySet() 明显多了一道工序。

keySet() 是不是就一无是处了?也不是。如果你在遍历的时候,压根儿就不关心Value,你只是想对所有的Key做点什么事,那用它就再合适不过了。比如,你想把所有的Key打印出来存档,或者把它们存到另一个集合里。这时候,它就显得干净利落。

只要宝贝不要钥匙:values(),目标明确

这个就更好理解了。有时候,你就是个“拜金主义者”,你只想要抽屉里的宝贝(Value),至于那些标签(Key)是啥,你毫不关心。

比如,你想计算所有商品的总价,或者找出班级里所有学生的名字列表。这时候,values() 就派上用场了。

java
// 伪代码示意
for (Integer value : map.values()) {
// 直接处理value,爽!
total += value;
}

这种场景下,用 values() 是最直接、最高效的。

新时代的风尚:Lambda表达式与forEach

自从Java 8开始,我们有了更时髦的玩法——Lambda。对于一个Map,你可以直接调用它的 forEach 方法。

java
// 伪代码示意
map.forEach((key, value) -> {
System.out.println("新姿势 -> 钥匙:" + key + ",宝贝:" + value);
});

这写法,一个字:!代码短得不像话,逻辑也清晰。它内部的实现,其实和 entrySet() 的遍历大同小异,所以效率上你不用担心。对于一些简单的遍历操作,我个人非常推荐这种写法,它能让你的代码看起来更现代,更有函数式编程的范儿。

真正的大坑:遍历时修改Map

好了,说了这么多遍历方法,现在得聊一个能让你代码当场爆炸的“天坑”了。那就是——在用for-each循环遍历一个HashMap的时候,尝试去修改它(比如增删元素)

你正开心地遍历着,突然,map.put() 或者 map.remove() 一下,然后,”啪”!一个 ConcurrentModificationException 劈头盖脸砸过来,程序直接崩掉。

这是Java集合框架的一种“快速失败”(fail-fast)机制。它就是为了防止你在一个不确定的集合状态下继续操作,导致更诡异的逻辑错误。

那怎么办?非要在遍历的时候删除某些元素不可呢?

救星来了——迭代器(Iterator)。这才是官方指定的、在遍历时安全删除元素的唯一正确姿势。

java
// 伪代码示意
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
if (满足某个删除条件) {
iterator.remove(); // 看清楚,是调用iterator的remove()!
}
}

记住这个铁律:要动刀子,就得通过迭代器iterator自己来,别绕过它去直接操作map

总而言之,遍历哈希表这事儿,看似简单,实则体现了一个程序员的基本功和代码素养。别再无脑用keySet()了,多想想你的真实需求。

  • 要Key也要Value? -> entrySet(),或者Java 8的 forEach
  • 只要Key? -> keySet()
  • 只要Value? -> values()
  • 遍历时要删除? -> 请务必、一定、必须使用 Iteratorremove() 方法!

搞懂了这些,你的代码质量,至少能上一个小台阶。


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注