龙空技术网

HashMap 七种遍历策略:性能对比与分析

陈青云 52

前言:

眼前看官们对“迭代器遍历hashmap”大致比较关注,咱们都想要知道一些“迭代器遍历hashmap”的相关知识。那么小编也在网上汇集了一些对于“迭代器遍历hashmap””的相关资讯,希望你们能喜欢,看官们快快来了解一下吧!

引言

在Java编程中,HashMap是一种常用的键值对数据结构,它提供了高效的查找、插入和删除操作。然而,当涉及到遍历大量数据时,不同的遍历方法对性能的影响不可忽视。本文将通过实验数据,对比分析七种遍历HashMap的方法,并探讨各自的优缺点。

方法与数据

在实验中,我们创建了一个包含500万条记录的HashMap,并使用Spring框架的StopWatch工具来测量每种遍历方法的耗时。以下是所测试的遍历方法及其平均执行时间(单位:纳秒):

使用迭代器遍历EntrySet - 平均耗时:约37毫秒使用迭代器遍历KeySet - 平均耗时:约42毫秒使用For Each遍历EntrySet - 平均耗时:约37毫秒使用For Each遍历KeySet - 平均耗时:约42毫秒使用Lambda表达式遍历 - 平均耗时:约57毫秒使用Streams API单线程遍历 - 平均耗时:约39毫秒使用Streams API多线程遍历 - 平均耗时:约24毫秒分析与比较迭代器遍历EntrySetFor Each遍历EntrySet: 这两种方法在性能上相近,因为它们都直接访问了HashMap的entrySet(),避免了额外的get()调用。选择哪种取决于个人编码习惯,但性能差异微乎其微。迭代器遍历KeySetFor Each遍历KeySet: 这两种方法的耗时略高,原因是每次循环都需要调用map.get(key)来获取值,这增加了额外的查找开销。使用Lambda表达式遍历: Lambda表达式的耗时最高,这主要是因为Lambda引入了额外的函数调用开销,尽管代码更加简洁,但在大规模数据集上效率较低。使用Streams API单线程遍历: Streams API提供了更现代的编程模型,允许使用函数式风格的代码。然而,单线程流并未显著提高性能,反而由于流的创建和处理增加了开销。使用Streams API多线程遍历: 当利用多线程时,Streams API展现出最佳性能,这是因为并行处理可以有效分摊计算负载,尤其在多核处理器上效果明显。但需要注意的是,多线程遍历可能引入线程安全问题,特别是在修改HashMap的情况下。结论

在遍历HashMap时,如果追求性能,建议使用迭代器遍历EntrySetFor Each遍历EntrySet,它们在大多数情况下提供了最佳平衡。而当处理数据集特别庞大且运行环境支持多线程时,使用Streams API多线程遍历则是最优选择,能够充分利用现代处理器的并行处理能力。不过,在选择遍历策略时,还需综合考虑代码可读性和维护性,以及具体的业务需求。

测试源码

import org.springframework.util.StopWatch;import java.util.HashMap;import java.util.Iterator;import java.util.Map;/** * @author CQY * @version 1.0 * @date 2024/7/18 14:29 **/public class TestHashMap {    public static void main(String[] args) {        // 创建一个 HashMap,写入500W条记录        HashMap<Integer, Object> map = new HashMap<>();        for (int i = 0; i < 500_0000; i++) {            map.put(i, "value" + i);        }        StopWatch stopWatch = new StopWatch("TestHashMap");        stopWatch.start("1.使用迭代器 EntrySet 的方式遍历");        Iterator<Map.Entry<Integer, Object>> iterator1 = map.entrySet().iterator();        while (iterator1.hasNext()) {            Map.Entry<Integer, Object> next = iterator1.next();            Integer key = next.getKey();            Object value = next.getValue();        }        stopWatch.stop();        stopWatch.start("2.使用迭代器的KeySet");        Iterator<Integer> iterator2 = map.keySet().iterator();        while (iterator2.hasNext()) {            Integer key = iterator2.next();            Object value = map.get(key);        }        stopWatch.stop();        stopWatch.start("3.使用 For Each EntrySet 的方式进行遍历");        for (Map.Entry<Integer, Object> entry : map.entrySet()) {            Integer key = entry.getKey();            Object value = entry.getValue();        }        stopWatch.stop();        stopWatch.start("4.使用 For Each KeySet 的方式进行遍历");        for (Integer key : map.keySet()) {            // key            Object value = map.get(key);        }        stopWatch.stop();        stopWatch.start("5.使用 Lambda 表达式的方式进行遍历");        map.forEach((key, value) -> {            // key  value        });        stopWatch.stop();        stopWatch.start("6.使用 Streams API 单线程的方式进行遍历");        map.entrySet().stream().forEach((integerStringEntry -> {            Integer key = integerStringEntry.getKey();            Object value = integerStringEntry.getValue();        }));        stopWatch.stop();        stopWatch.start("7.使用 Streams API 多线程的方式进行遍历");        map.entrySet().parallelStream().forEach((integerStringEntry -> {            Integer key = integerStringEntry.getKey();            Object value = integerStringEntry.getValue();        }));        stopWatch.stop();        System.out.println(stopWatch.prettyPrint());    }}
StopWatch耗时打印
StopWatch 'TestHashMap': running time = 279937200 ns---------------------------------------------ns         %     Task name---------------------------------------------036545100  013%  1.使用迭代器 EntrySet 的方式遍历042892700  015%  2.使用迭代器的KeySet037677900  013%  3.使用 For Each EntrySet 的方式进行遍历042370800  015%  4.使用 For Each KeySet 的方式进行遍历057247300  020%  5.使用 Lambda 表达式的方式进行遍历039540800  014%  6.使用 Streams API 单线程的方式进行遍历023662600  008%  7.使用 Streams API 多线程的方式进行遍历StopWatch 'TestHashMap': running time = 276661500 ns---------------------------------------------ns         %     Task name---------------------------------------------035001300  013%  1.使用迭代器 EntrySet 的方式遍历043237200  016%  2.使用迭代器的KeySet037038400  013%  3.使用 For Each EntrySet 的方式进行遍历043772700  016%  4.使用 For Each KeySet 的方式进行遍历057677600  021%  5.使用 Lambda 表达式的方式进行遍历036356600  013%  6.使用 Streams API 单线程的方式进行遍历023577700  009%  7.使用 Streams API 多线程的方式进行遍历StopWatch 'TestHashMap': running time = 318431400 ns---------------------------------------------ns         %     Task name---------------------------------------------044152200  014%  1.使用迭代器 EntrySet 的方式遍历051133000  016%  2.使用迭代器的KeySet047733600  015%  3.使用 For Each EntrySet 的方式进行遍历051962300  016%  4.使用 For Each KeySet 的方式进行遍历058811800  018%  5.使用 Lambda 表达式的方式进行遍历036561500  011%  6.使用 Streams API 单线程的方式进行遍历028077000  009%  7.使用 Streams API 多线程的方式进行遍历

标签: #迭代器遍历hashmap