热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

HashMap遍历方式性能比较

JDK8之前,可以使用ketSet或者entrySet来遍历HashMap,JDK8中引入了map.forEach来进行遍历1.keySet和entrySet1.1基本用法keyS

JDK8之前,可以使用ketSet或者entrySet来遍历HashMap,JDK8中引入了map.forEach来进行遍历

1. keySet和entrySet

1.1 基本用法

keySet:

Map map = new HashMap<>();
Iterator it = map.keySet().iterator();
Object key;
Object value;
while(it.hasNext()){
key = it.next();
value = map.get(key);//性能差异
System.out.println(key+":"+value);
}

entrySet:

Map map = new HashMap<>();
Iterator it = map.entrySet().iterator();
Object key;
Object value;
while(it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
key = entry.getKey();
value = entry.getValue();//性能差异
System.out.println(key+":"+value);
}

1.2 源码分析

keySet:

public Set keySet() {
Set ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
final class KeySet extends AbstractSet {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer action) {
Node[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i for (Node e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}

entrySet

public Set> entrySet() {
Set> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
final class EntrySet extends AbstractSet> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry) o;
Object key = e.getKey();
Node candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry e = (Map.Entry) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer> action) {
Node[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i for (Node e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}

从源码中可以看出,当要得到某个value时,keySet需要从hashMap中get,entrySet相比keySet少了遍历table的过程,这就是两者性能上的主要差别

map.forEach()

default void forEach(BiConsumer action) {
Objects.requireNonNull(action);
for (Map.Entry entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}

源码分析可知,map.foreach()本质上还是使用的entrySet

实例

package com.feiyu.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @author maokeluo
* @desc 遍历hashMap性能比较
* @create 17-9-2
*/
public class TraverseHsahMap {
public static void main(String[] args) throws InterruptedException {
Map map = new HashMap();
int num = 10000000;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i String temp = String.valueOf(i);
map.put(temp,temp);
}
}
});
thread.start();
thread.join();

//forEach map.entrySet
long start1 = System.currentTimeMillis();
for (Map.Entry entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
long end1 = System.currentTimeMillis() - start1;
System.out.println("forEach map.entrySet遍历HashMap使用时间:"+end1);
//显式调用map.entrySet()的集合迭代器
long start2 = System.currentTimeMillis();
Iterator> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = iterator.next();
entry.getKey();
entry.getValue();
}
long end2 = System.currentTimeMillis() - start2;
System.out.println("显式调用map.entrySet()的集合迭代器遍历HashMap使用时间:"+end2);
//forEach map.keySet在调用get取值
long start3 = System.currentTimeMillis();
for (String key : map.keySet()) {
map.get(key);
}
long end3 = System.currentTimeMillis() - start3;
System.out.println("forEach map.keySet在调用get取值遍历HashMap使用时间:"+end3);
//forEach map.entrySet(),用临时变量保存map.entrySet()
long start4 = System.currentTimeMillis();
Set> entrySet = map.entrySet();
for (Map.Entry entry : entrySet) {
entry.getKey();
entry.getValue();
}
long end4 = System.currentTimeMillis() - start4;
System.out.println("forEach map.entrySet(),用临时变量保存map.entrySet()使用时间:"+end4);
}
}

结果

| map size | 10000 | 100000 | 1000000 | 10000000 |
|&#8212;&#8212;&#8211;|&#8212;&#8212;&#8211;|
| forEach entrySet | 2ms | 11ms | 32ms | 199ms |
| for iterator entrySet| 1ms | 5ms | 29ms | 203ms |
| forEach keySet | 2ms | 7ms | 54ms | 273ms |
| for entrySet=entrySet()| 1ms | 5ms | 28ms | 189ms |

遍历方式结论总结

  • hashMap的循环遍历,如果不仅需要key又需要value,使用

for (Map.Entry entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}

效率更高

  • 如果只是遍历key不需要value,可以直接使用

for (String key : map.keySet()) {
//操作key
}

推荐阅读
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 本文详细探讨了MySQL数据库实例化参数的优化方法及其在实例查询中的应用。通过具体的源代码示例,介绍了如何高效地配置和查询MySQL实例,为开发者提供了有价值的参考和实践指导。 ... [详细]
  • 机器学习算法:SVM(支持向量机)
    SVM算法(SupportVectorMachine,支持向量机)的核心思想有2点:1、如果数据线性可分,那么基于最大间隔的方式来确定超平面,以确保全局最优, ... [详细]
  • 重要知识点有:函数参数默许值、盈余参数、扩大运算符、new.target属性、块级函数、箭头函数以及尾挪用优化《深切明白ES6》笔记目次函数的默许参数在ES5中,我们给函数传参数, ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 本文总结了Java初学者需要掌握的六大核心知识点,帮助你更好地理解和应用Java编程。无论你是刚刚入门还是希望巩固基础,这些知识点都是必不可少的。 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • Flowable 流程图路径与节点展示:已执行节点高亮红色标记,增强可视化效果
    在Flowable流程图中,通常仅显示当前节点,而路径则需自行获取。特别是在多次驳回的情况下,节点可能会出现混乱。本文重点探讨了如何准确地展示流程图效果,包括已结束的流程和正在执行的流程。具体实现方法包括生成带有高亮红色标记的图片,以增强可视化效果,确保用户能够清晰地了解每个节点的状态。 ... [详细]
  • Silverlight 实战指南:深入解析用户提交数据的验证与捕获机制
    本文深入探讨了Silverlight中用户提交数据的验证与捕获机制,详细分析了四种主要的验证方法:基本异常处理、DataAnnotation注解、IDataErrorInfo客户端同步验证以及自定义验证策略。通过实例解析,帮助开发者更好地理解和应用这些机制,提升应用程序的数据处理能力和用户体验。 ... [详细]
  • 本文将继续探讨 JavaScript 函数式编程的高级技巧及其实际应用。通过一个具体的寻路算法示例,我们将深入分析如何利用函数式编程的思想解决复杂问题。示例中,节点之间的连线代表路径,连线上的数字表示两点间的距离。我们将详细讲解如何通过递归和高阶函数等技术实现高效的寻路算法。 ... [详细]
  • 在Kohana 3框架中,实现最优的即时消息显示方法是许多开发者关注的问题。本文将探讨如何高效、优雅地展示flash消息,包括最佳实践和技术细节,以提升用户体验和代码可维护性。 ... [详细]
  • 如何使用和示例代码解析 org.semanticweb.owlapi.model.OWLSubPropertyChainOfAxiom.getPropertyChain() 方法 ... [详细]
  • 本次发布的Qt音乐播放器2.0版本在用户界面方面进行了细致优化,提升了整体的视觉效果和用户体验。尽管核心功能与1.0版本保持一致,但界面的改进使得操作更加直观便捷,为用户带来了更为流畅的使用体验。此外,我们还对部分细节进行了微调,以确保软件的稳定性和性能得到进一步提升。 ... [详细]
author-avatar
晨风流云
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有