java中HashMap的7种遍历方式与性能分析

目录
  • 1、遍历方式
    • 1.1 迭代器 EntrySet
    • 1.2 迭代器 KeySet
    • 1.3 ForEach EntrySet
    • 1.4 ForEach KeySet
    • 1.5 Lambda 表达式
    • 1.6 Stream API 单线程
    • 1.7 Stream API 多线程
    • 1.8 代码汇总
  • 2、性能分析
    • 2.1 引入依赖
    • 2.2 编写测试类
    • 2.3 测试结果
    • 2.4 分析
    • 2.5 总结

1、遍历方式

1.1 迭代器 EntrySet

/**
 * 1. 迭代器 EntrySet
 */
@Test
public void test1() {
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "Java");
    map.put(2, "JavaSE");
    map.put(3, "JavaEE");
    map.put(4, "Spring");
    map.put(5, "SpringMVC");
    map.put(6, "MyBatis");

    Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry<Integer, String> entry = iterator.next();
        System.out.println(entry.getKey() + ":" + entry.getValue());
    }
}

1.2 迭代器 KeySet

/**
 * 2. 迭代器 KeySet
 */
@Test
public void test2() {
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "Java");
    map.put(2, "JavaSE");
    map.put(3, "JavaEE");
    map.put(4, "Spring");
    map.put(5, "SpringMVC");
    map.put(6, "MyBatis");

    Iterator<Integer> iterator = map.keySet().iterator();
    while (iterator.hasNext()) {
        Integer key = iterator.next();
        System.out.println(key + ":" + map.get(key));
    }
}

1.3 ForEach EntrySet

/**
 * 3. ForEach EntrySet
 */
@Test
public void test3() {
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "Java");
    map.put(2, "JavaSE");
    map.put(3, "JavaEE");
    map.put(4, "Spring");
    map.put(5, "SpringMVC");
    map.put(6, "MyBatis");

    for (Map.Entry<Integer, String> entry : map.entrySet()) {
        System.out.println(entry.getKey() + ":" + entry.getValue());
    }
}

1.4 ForEach KeySet

/**
 * 4. ForEach KeySet
 */
@Test
public void test4() {
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "Java");
    map.put(2, "JavaSE");
    map.put(3, "JavaEE");
    map.put(4, "Spring");
    map.put(5, "SpringMVC");
    map.put(6, "MyBatis");

    for (Integer key : map.keySet()) {
        System.out.println(key + ":" + map.get(key));
    }
}

1.5 Lambda 表达式

/**
 * 5. Lambda 表达式
 */
@Test
public void test5() {
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "Java");
    map.put(2, "JavaSE");
    map.put(3, "JavaEE");
    map.put(4, "Spring");
    map.put(5, "SpringMVC");
    map.put(6, "MyBatis");

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

1.6 Stream API 单线程

/**
 * 6. Stream API 单线程
 */
@Test
public void test6() {
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "Java");
    map.put(2, "JavaSE");
    map.put(3, "JavaEE");
    map.put(4, "Spring");
    map.put(5, "SpringMVC");
    map.put(6, "MyBatis");

    map.entrySet().stream().forEach((entry) -> {
        System.out.println(entry.getKey() + ":" + entry.getValue());
    });
}

1.7 Stream API 多线程

/**
 * 7. Stream API 多线程
 */
@Test
public void test7() {
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "Java");
    map.put(2, "JavaSE");
    map.put(3, "JavaEE");
    map.put(4, "Spring");
    map.put(5, "SpringMVC");
    map.put(6, "MyBatis");

    map.entrySet().parallelStream().forEach((entry) -> {
        System.out.println(entry.getKey() + ":" + entry.getValue());
    });
}

1.8 代码汇总

/**
 * HashMap 的 7 种遍历方式
 * @ClassName HashMapTraverse
 * @Author YH
 * @Date 2021/11/12
 * @Version 1.0
 */
public class HashMapTraverseTest {

    /**
     * 1. 迭代器 EntrySet
     */
    @Test
    public void test1() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JavaSE");
        map.put(3, "JavaEE");
        map.put(4, "Spring");
        map.put(5, "SpringMVC");
        map.put(6, "MyBatis");

        Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, String> entry = iterator.next();
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }

    /**
     * 2. 迭代器 KeySet
     */
    @Test
    public void test2() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JavaSE");
        map.put(3, "JavaEE");
        map.put(4, "Spring");
        map.put(5, "SpringMVC");
        map.put(6, "MyBatis");

        Iterator<Integer> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            Integer key = iterator.next();
            System.out.println(key + ":" + map.get(key));
        }
    }

    /**
     * 3. ForEach EntrySet
     */
    @Test
    public void test3() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JavaSE");
        map.put(3, "JavaEE");
        map.put(4, "Spring");
        map.put(5, "SpringMVC");
        map.put(6, "MyBatis");

        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }

    /**
     * 4. ForEach KeySet
     */
    @Test
    public void test4() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JavaSE");
        map.put(3, "JavaEE");
        map.put(4, "Spring");
        map.put(5, "SpringMVC");
        map.put(6, "MyBatis");

        for (Integer key : map.keySet()) {
            System.out.println(key + ":" + map.get(key));
        }
    }

    /**
     * 5. Lambda 表达式
     */
    @Test
    public void test5() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JavaSE");
        map.put(3, "JavaEE");
        map.put(4, "Spring");
        map.put(5, "SpringMVC");
        map.put(6, "MyBatis");

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

    /**
     * 6. Stream API 单线程
     */
    @Test
    public void test6() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JavaSE");
        map.put(3, "JavaEE");
        map.put(4, "Spring");
        map.put(5, "SpringMVC");
        map.put(6, "MyBatis");

        map.entrySet().stream().forEach((entry) -> {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        });
    }

    /**
     * 7. Stream API 多线程
     */
    @Test
    public void test7() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JavaSE");
        map.put(3, "JavaEE");
        map.put(4, "Spring");
        map.put(5, "SpringMVC");
        map.put(6, "MyBatis");

        map.entrySet().parallelStream().forEach((entry) -> {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        });
    }
}

2、性能分析

使用 Oracle 官方提供的性能测试工具 JMH(Java Microbenchmark Harness,JAVA 微基准测试套件)来测试一下这 7 种循环的性能。

使用 JMH 进行性能基准测试

2.1 引入依赖

<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.23</version>
    <scope>provided</scope>
</dependency>

2.2 编写测试类

直接复制粘贴即可

/**
 * @ClassName HashMapCycleTest
 * @Author YH
 * @Date 2021/11/12
 * @Version 1.0
 */
@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 2 轮,每次 1s
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 1s
@Fork(1) // fork 1 个线程
@State(Scope.Thread) // 每个测试线程一个实例
public class HashMapCycleTest {
    /**
     * 类加载时赋值
     */
    static Map<Integer, String> map = new HashMap() {{
        // 添加数据
        for (int i = 0; i < 100; i++) {
            put(i, "val:" + i);
        }
    }};

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                // 要导入的测试类
                .include(HashMapCycleTest.class.getSimpleName())
                // 输出测试结果的文件
                .output("D:/JAVA/面试/workplace/interview/jmh-hashMap.log")
                .build();
        // 执行测试
        new Runner(opt).run();
    }

    /**
     * 迭代器 EntrySet
     */
    @Benchmark
    public void entrySet() {
        // 遍历
        Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, String> entry = iterator.next();
            Integer k = entry.getKey();
            String v = entry.getValue();
        }
    }

    /**
     * ForEach EntrySet
     */
    @Benchmark
    public void forEachEntrySet() {
        // 遍历
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            Integer k = entry.getKey();
            String v = entry.getValue();
        }
    }

    /**
     * 迭代器 KeySet
     */
    @Benchmark
    public void keySet() {
        // 遍历
        Iterator<Integer> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            Integer k = iterator.next();
            String v = map.get(k);
        }
    }

    /**
     * ForEach KeySet
     */
    @Benchmark
    public void forEachKeySet() {
        // 遍历
        for (Integer key : map.keySet()) {
            Integer k = key;
            String v = map.get(k);
        }
    }

    /**
     * Lambda 表达式
     */
    @Benchmark
    public void lambda() {
        // 遍历
        map.forEach((key, value) -> {
            Integer k = key;
            String v = value;
        });
    }

    /**
     * Stream API 单线程
     */
    @Benchmark
    public void streamApi() {
        // 单线程遍历
        map.entrySet().stream().forEach((entry) -> {
            Integer k = entry.getKey();
            String v = entry.getValue();
        });
    }

    /**
     * Stream API 多线程
     * 这个不用测,可以肯定性能是最好的。
     * 如果把这个加入进测试了,理论上来说性能应该是最差的(已经测试过)
     * 为啥这么说?因为你本来就开了好几个线程来测试其他方法了,
     * 你这个方法还想再多搞几个线程来提升性能已经不可能了,线程都分配完了。
     * 线程上下文切换的时间会更长!!!所以不能一起测试!!!
     */
    public void parallelStreamApi() {
        // 多线程遍历
        map.entrySet().parallelStream().forEach((entry) -> {
            Integer k = entry.getKey();
            String v = entry.getValue();
        });
    }
}

2.3 测试结果

运行程序,查看输出日志

(1)第一次

(2)第二次

(3)第三次

2.4 分析

上图解释:测试结论{测试的方法(Benchmark)、测试类型(Mode)、测试总次数(Cnt)、测试结果(Score)、误差(Error)、单位(Units)}

其中 Units 为 ns/op 意思是执行完成时间(单位为纳秒),而 Score 列为平均执行时间, ± 符号表示误差。

从以上结果可以看出,Lambda 和两个 EntrySet 的性能相近,接下来是 Stream API 单线程,然后是 KeySet,性能最差。

2.5 总结

从以上结果可以看出 entrySet 的性能比 keySet 的性能高出了一倍之多,因此我们应该尽量使用 entrySet 来实现 Map 集合的遍历,当然,如果熟练 LambdaLambda 更好咯,毕竟代码简洁。

如果想深入了解为啥性能会差别这么大,建议查看字节码文件进行分析。或者是使用 javap -c 类名.class 进行反编译,查看底层的实现。

到此这篇关于java中HashMap的7种遍历方式与性能分析的文章就介绍到这了,更多相关java HashMap遍历 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java5种遍历HashMap数据的写法

    本文介绍了最好的Java5种遍历HashMap数据的写法,分享给大家,也给自己留一个笔记,具体如下: 通过EntrySet的迭代器遍历 Iterator < Entry < Integer, String >> iterator = coursesMap.entrySet().iterator(); while (iterator.hasNext()) { Entry < Integer, String > entry = iterator.next(); System

  • Java HashMap三种循环遍历方式及其性能对比实例分析

    本文实例讲述了Java HashMap三种循环遍历方式及其性能对比.分享给大家供大家参考,具体如下: HashMap的三种遍历方式 (1)for each map.entrySet() Map<String, String> map = new HashMap<String, String>(); for (Entry<String, String> entry : map.entrySet()) { entry.getKey(); entry.getValue();

  • Java8 HashMap遍历方式性能探讨

    原因: keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value.而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高.如果是JDK8,使用Map.foreach方法. 一. keySet和entrySet Map<String, String> map = new HashMap<String, String>(); int num = 5000000; String key, value

  • Java开发之HashMap的使用和遍历

    Java开发之HashMap的使用和遍历 1:使用HashMap的一个简单例子 package com.pb.collection; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.Map.Entry; public class HashMapDemo { public static void main(String[] args) { HashMap<Stri

  • java遍历HashMap简单的方法

    本文实例讲述了java遍历HashMap简单的方法.分享给大家供大家参考.具体实现方法如下: import java.util.HashMap; import java.util.Iterator; import java.util.Set; public class HashSetTest { public static void main(String[] args) { HashMap map = new HashMap(); map.put("a", "aa"

  • Java HashMap 如何正确遍历并删除元素的方法小结

    (一)HashMap的遍历 HashMap的遍历主要有两种方式: 第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况. HashMap<K, V> myHashMap; for (Map.entry<K, V> item : myHashMap.entrySet()){ K key = item.getKey(); V val = item.getValue(); //todo with key and val //WARNI

  • java中HashMap的7种遍历方式与性能分析

    目录 1.遍历方式 1.1 迭代器 EntrySet 1.2 迭代器 KeySet 1.3 ForEach EntrySet 1.4 ForEach KeySet 1.5 Lambda 表达式 1.6 Stream API 单线程 1.7 Stream API 多线程 1.8 代码汇总 2.性能分析 2.1 引入依赖 2.2 编写测试类 2.3 测试结果 2.4 分析 2.5 总结 1.遍历方式 1.1 迭代器 EntrySet /** * 1. 迭代器 EntrySet */ @Test pu

  • 浅谈HashMap中7种遍历方式的性能分析

    目录 一.前言 二.HashMap遍历 2.1.迭代器EntrySet 2.2.迭代器 KeySet 2.3.ForEachEntrySet 2.4.ForEach KeySet 2.5.Lambda 2.6.Streams API 单线程 2.7.Streams API 多线程 三.性能分析 四.字节码分析 五.EntrySet性能分析 六.安全性测试 6.1.迭代器方式 6.2.For 循环方式 6.3.Lambda 方式 6.4.Stream 方式 6.5.小结 七.总结 一.前言 随着

  • java 单例的五种实现方式及其性能分析

    java 单例的五种实现方式及其性能分析 序言 在23种设计模式中,单例是最简单的设计模式,但是也是很常用的设计模式.从单例的五种实现方式中我们可以看到程序员对性能的不懈追求.下面我将分析单例的五种实现方式的优缺点,并对其在多线程环境下的性能进行测试. 实现 单例模式适用于资源占用较多的类,保证一个类只有一个实例即单例.通用的做法就是构造器私有化,提供一个全局的访问点,返回类的实例. uml图: 1.饿汉式 代码实现: package com.zgh.gof23.singleton; /** *

  • Java中switch的三种用法方式

    Java中switch的三种用法详解: switch居然有三种方式 ? 作为一个接触java不久的人来说,这确实让我吃了一惊! 根据版本,在java14开始, switch语句有了一个很大的调整, 这就让swicth语句有了更多的操作和选择,在代码上,更加的简便灵活, 让我们试试这神奇的switch吧! 使用switch这个关键词, 我们可以很好的解决if-else 中多重选择的尴尬场面! Java中switch的三种用法详解: switch 标准方式 switch - > 用法: switch

  • Java中mybatis的三种分页方式

    目录 前言 一.Limit分页 二.RowBounds分页(不推荐使用) 三.Mybatis_PageHelper分页插件 前言 分页是我们在开发中绕不过去的一个坎!当你的数据量大了的时候,一次性将所有数据查出来不现实,所以我们一般都是分页查询的,减轻服务端的压力,提升了速度和效率!也减轻了前端渲染的压力! 注意:由于 java 允许的最大整数为 2147483647,所以 limit 能使用的最大整数也是 2147483647,一次性取出大量数据可能引起内存溢出,所以在大数据查询场合慎重使用!

  • 浅谈java中String的两种赋值方式的区别

    类似普通对象,通过new创建字符串对象.String str = new String("Hello"); 内存图如下图所示,系统会先创建一个匿名对象"Hello"存入堆内存(我们暂且叫它A),然后new关键字会在堆内存中又开辟一块新的空间,然后把"Hello"存进去,并且把地址返回给栈内存中的str, 此时A对象成为了一个垃圾对象,因为它没有被任何栈中的变量指向,会被GC自动回收. 直接赋值.如String str = "Hello&

  • Java中Singleton的3种实现方式详解

    一.什么是Singleton? <设计模式>的作者.Eclipse和 Junit 的开发者 Erich Gamma 在它的理论体系中将 Singleton 定义为仅仅被实例化一次的类.在当今面向对象程序的实际开发中,Singleton 通常被用来代表一个无状态的对象,例如函数和那些本质上唯一的系统组件. 值得注意的是,使类成为 Singleton 会使得它的客户端测试变得非常困难,因为我们不可能给Singleton替换模拟实现,除非我们实现一个充当其类型的接口. 实现 Singleton 有三

  • Java中定时任务的6种实现方式

    目录 1.线程等待实现 2.JDK自带Timer实现 2.1 核心方法 2.2使用示例 2.2.1指定延迟执行一次 2.2.2固定间隔执行 2.2.3固定速率执行 2.3 schedule与scheduleAtFixedRate区别 2.3.1schedule侧重保持间隔时间的稳定 2.3.2scheduleAtFixedRate保持执行频率的稳定 2.4 Timer的缺陷 3.JDK自带ScheduledExecutorService 3.1 scheduleAtFixedRate方法 3.2

  • JS常用的几种数组遍历方式以及性能分析对比实例详解

    本文实例讲述了JS常用的几种数组遍历方式以及性能分析对比.分享给大家供大家参考,具体如下: 前言 这一篇与上一篇 JS几种变量交换方式以及性能分析对比 属于同一个系列,本文继续分析JS中几种常用的数组遍历方式以及各自的性能对比 起由 在上一次分析了JS几种常用变量交换方式以及各自性能后,觉得这种方式挺好的,于是抽取了核心逻辑,封装成了模板,打算拓展成一个系列,本文则是系列中的第二篇,JS数组遍历方式的分析对比 JS数组遍历的几种方式 JS数组遍历,基本就是for,forin,foreach,fo

  • Java中HashMap的初始容量设置方式

    Java中HashMap的初始容量设置 根据阿里巴巴Java开发手册上建议HashMap初始化时设置已知的大小,如果不超过16个,那么设置成默认大小16: 集合初始化时, 指定集合初始值大小. 说明: HashMap使用HashMap(int initialCapacity)初始化 正例: initialCapacity = (需要存储的元素个数 / 负载因子) + 1.注意负载因子(即loader factor)默认为0.75, 如果暂时无法确定初始值大小,请设置为16(即默认值). 反例:

随机推荐