行业资讯 2025年08月6日
0 收藏 0 点赞 147 浏览 3190 个字
摘要 :

文章目录 1.引言 2.遍历Map的不同方式 2.1. HashMap.forEach() 2.2. 遍历Map的条目 2.3. 遍历Map的键并访问值 2.4. 使用迭代器 3.比较不同技术的性能 4.结果 5.结论 ……




  • 1.引言
  • 2.遍历Map的不同方式
    • 2.1. HashMap.forEach()
    • 2.2. 遍历Map的条目
    • 2.3. 遍历Map的键并访问值
    • 2.4. 使用迭代器
  • 3.比较不同技术的性能
  • 4.结果
  • 5.结论

这篇Java教程讨论了不同遍历HashMap的方式,并比较了每种方式的性能,以便我们知道HashMap遍历效率最高的方法是哪种。

1.引言

在这篇文章中,我决定比较不同遍历HashMap的方式的性能。HashMap是一个广泛使用的类,大多数情况下,我们使用该类提供的get(Object key)方法来获取值。但有时需要遍历整个Map并获取其中存储的所有键-值对。

例如,分析从客户端发送的所有请求参数。在这种情况下,对于每个客户端,我们至少在请求处理期间遍历整个映射。

如果我们在代码中的许多地方都使用这种类型的迭代,而且有许多请求,那么我们肯定希望优化HashMap迭代代码,以充分利用它。我下面的分析将帮助我们决定下一步该做什么。

2.遍历Map的不同方式

让我们从不同遍历HashMap的方式开始。HashMap定义如下:

Map<String, Integer> map = new HashMap();

2.1. HashMap.forEach()

自Java 8以来,我们可以使用forEach()方法遍历Map中存储的键和值。

map.forEach((key, value) -> {
    System.out.println(key + \": \" + value);
    //...
});

2.2. 遍历Map的条目

entrySet()方法返回一个Set视图,其中包含所有的条目,我们可以像普通的HashSet一样遍历它。

for (Map.Entry<String, Integer> entry : map.entrySet()) {
  String key = entry.getKey();
  Integer value = entry.getValue();
  //...
}

2.3. 遍历Map的键并访问值

keySet()方法返回所有键的集合。我们可以遍历所有键,并使用它们来访问Map中的值。请注意,这需要额外的步骤来访问值,所以在大多数情况下,这不是推荐的方法。

for (String key : map.keySet()) {
  Integer value = map.get(key);
  //...
}

2.4. 使用迭代器

由于我们从entrySet()和keySet()获取Set视图,因此我们可以使用迭代器遍历Map。

以下代码使用迭代器遍历Map条目。

Iterator<Entry<String, Integer>> entryIterator = map.entrySet().iterator();
while (entryIterator.hasNext()) {
  Map.Entry<String, Integer> entry = entryIterator.next();
  String key = entry.getKey();
  Integer value = entry.getValue();
  //...
}

同样,以下代码使用迭代器遍历Map的键,并使用键来访问值。

Iterator<String> keySetIterator = map.keySet().iterator();
while (keySetIterator.hasNext()) {
  String key = keySetIterator.next();
  Integer value = map.get(key);
  //...
}

3.比较不同技术的性能

现在让我们比较对Map中存储的常见数据集执行所有类型的遍历的性能。我们在Map中存储了100万个键-值对。

Map<String, Integer> map = new HashMap();
for (int i = 0; i < 10_00_000; i++) {
  map.put(String.valueOf(i), i);
}

我们将以四种方式遍历Map。我们还将以最合适的方式从Map中获取所有条目的键和值。我们使用JMH模块来对所有方法的性能进行基准测试。

@Benchmark
public void usingForEach(Blackhole blackhole) {
  map.forEach((key, value) -> {
    blackhole.consume(key);
    blackhole.consume(value);
  });
}
@Benchmark
public void usingEntrySetWithForLoop(Blackhole blackhole) {
  for (Map.Entry<String, Integer> entry : map.entrySet()) {
    blackhole.consume(entry.getKey());
    blackhole.consume(entry.getValue());
  }
}
@Benchmark
public void usingKeySetWithForLoop(Blackhole blackhole) {
  for (String key : map.keySet()) {
    blackhole.consume(map.get(key));
  }
}
@Benchmark
public void usingEntrySetWithForIterator(Blackhole blackhole) {
  Iterator<Entry<String, Integer>> entryIterator = map.entrySet().iterator();
  while (entryIterator.hasNext()) {
    Map.Entry<String, Integer> entry = entryIterator.next();
    blackhole.consume(entry.getKey());
    blackhole.consume(entry.getValue());
  }
}

4.结果

以上程序的基准测试得分是:

c.h.c.collections.map...usingForEach                     thrpt   15    89.044 ±   2.703  ops/s
c.h.c.collections.map...usingEntrySetWithForLoop         thrpt   15    54.906 ±   6.326  ops/s
c.h.c.collections.map...usingKeySetWithForLoop          thrpt   15    52.163 ±   4.517  ops/s
c.h.c.collections.map...usingEntrySetWithForIterator      thrpt   15    63.494 ±   4.334  ops/s

虽然所有方法之间的差异不大,但我们可以看到:

  • 使用for循环与keySet()、entrySet()方法几乎表现相同。
  • 使用iterator()方法性能较差。
  • 令人惊讶的是,forEach()方法需要最长的时间。这是因为forEach方法使用内部迭代,这意味着迭代逻辑在HashMap实现内部处理。它为HashMap中的每个元素调用提供的lambda表达式。这种内部迭代会带来一些开销,因为涉及到lambda表达式的执行和额外的函数调用。

出于代码的清晰性,我们可以得出以下结论:

  • 如果Map仅包含少量条目,则使用HashMap.forEach()是遍历HashMap的最佳方式。
  • 如果Map包含100万个条目,那么使用HashMap.entrySet()是遍历HashMap的最佳方式。

5.结论

对于大多数应用需求来说,100万是一个非常大的数字。尽管与毫秒级的差异相比,并不是非常显著,但与for循环的情况相比,差异很大。我认为大多数人可以容忍这种微小的差异。

但如果你想得出结论,使用entry set非常明确,并且比其他方法性能更好。当多次执行上述程序时,结果的变化在20%到50%之间。

扩展阅读:Java中多种循环性能的区别比较

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/9385.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

986
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

463
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

347
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

455
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

515
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

831
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号