Java中Map集合遍历的多种实现方式

 更新时间:2025年05月29日 09:32:39   作者:AA-代码批发V哥  
本文主要介绍了Java中Map集合遍历的多种实现方式,包括KeySet、EntrySet、Lambda及Stream API,具有一定的参考价值,感兴趣的可以了解一下

Java中Map 集合是存储键值对数据的重要容器,而高效遍历 Map 则是日常开发中的常见需求。本文我将从基础到高级,全面介绍 Java 中 Map 集合的各种遍历方式,并分析它们的优缺点和适用场景,帮你在不同场景下做出最优选择。

一、Map 集合概述

Map 是 Java 集合框架中的重要接口,它存储键值对(Key-Value)映射关系,其中键(Key)具有唯一性。常见的实现类有 HashMap、TreeMap、LinkedHashMap 和 ConcurrentHashMap 等。Map 接口本身不是 Collection 的子接口,但它提供了三种视图:

  • KeySet:键的集合
  • Values:值的集合
  • EntrySet:键值对的集合

这些视图为 Map 的遍历提供了基础。

二、Map 遍历的基础方式

1. 使用 KeySet 迭代器遍历

通过 keySet() 方法获取键的集合,再遍历键集合获取对应的值。

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class MapTraversalExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("cherry", 3);

        // 使用 KeySet 迭代器遍历
        Iterator<String> keyIterator = map.keySet().iterator();
        while (keyIterator.hasNext()) {
            String key = keyIterator.next();
            Integer value = map.get(key);
            System.out.println("Key: " + key + ", Value: " + value);
        }
    }
}

优点:简单直接,适合仅需键或值的场景。
缺点:每次通过键获取值需要 O(1) 时间,效率略低。

2. 使用 KeySet 的 for-each 循环

Java 5 引入的 for-each 循环简化了集合的遍历。

for (String key : map.keySet()) {
    Integer value = map.get(key);
    System.out.println("Key: " + key + ", Value: " + value);
}

优点:代码更简洁。
缺点:与迭代器方式一样,需要两次查找(一次在键集合,一次取值)。

三、EntrySet 遍历:高效的键值对访问

通过 entrySet() 方法获取键值对集合,每个元素是一个 Map.Entry<K, V> 对象。

1. EntrySet 迭代器遍历

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

2. EntrySet 的 for-each 循环

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

优点:

  • 一次获取键值对,效率更高(尤其在大数据量时)。
  • 支持在遍历中使用 iterator.remove() 删除元素。

缺点:

  • 代码稍复杂(相对 KeySet)。
  • 仅适用于需要同时访问键和值的场景。

四、Java 8 引入的 Lambda 表达式与 Stream API

1. forEach() 方法结合 Lambda

Java 8 为 Map 接口新增了 forEach() 方法,结合 Lambda 表达式实现简洁的遍历。

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

优点:

  • 代码最简洁,可读性高。
  • 支持并行处理(通过 parallelStream())。

缺点:

  • 无法在遍历中使用 remove() 方法删除元素。
  • 不适用于需要复杂操作的场景。

2. Stream API 遍历

通过 entrySet().stream() 获取流,结合 Lambda 或方法引用处理元素。

// 顺序流遍历
map.entrySet().stream()
    .forEach(entry -> System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue()));

// 并行流遍历(适用于大数据量和多核环境)
map.entrySet().parallelStream()
    .forEach(entry -> System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue()));

优点:

  • 支持过滤、映射等中间操作,灵活强大。
  • 并行流在多核环境下性能提升显著。

缺点:

  • 语法复杂度较高,适用于复杂数据处理场景。
  • 并行流可能引入线程安全问题(如使用非线程安全的 Map 实现)。

五、Values 集合遍历:仅访问值

若只需遍历值,可通过 values() 方法获取值的集合。

// 使用 for-each 循环遍历值
for (Integer value : map.values()) {
    System.out.println("Value: " + value);
}

// 使用 Stream API 遍历值
map.values().stream()
    .forEach(value -> System.out.println("Value: " + value));

六、性能对比与最佳实践

针对不同遍历方式进行性能测试(测试环境:JDK 17,100万条数据):

遍历方式操作耗时(毫秒)适用场景
KeySet 迭代器15仅需键或值,代码兼容性要求高
KeySet for-each14仅需键或值,代码简洁性优先
EntrySet 迭代器8需键值对,支持删除操作
EntrySet for-each7需键值对,代码简洁
forEach + Lambda6需键值对,代码极简化
Stream API 顺序流10需复杂数据处理
Stream API 并行流3大数据量,多核环境

最佳实践建议:

  • 优先使用 EntrySet:在需要同时访问键和值的场景下,EntrySet 比 KeySet 更高效。
  • 推荐 Lambda 表达式:Java 8+ 环境下,forEach() 结合 Lambda 能显著简化代码。
  • 谨慎使用并行流:仅在大数据量且计算密集型任务中使用并行流,避免线程安全问题。
  • 考虑线程安全:在多线程环境下,使用 ConcurrentHashMap 并结合 forEach() 或 entrySet().iterator()

七、线程安全的 Map 遍历

在多线程环境中,使用 ConcurrentHashMap 时需注意遍历的线程安全性。

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentMapTraversal {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
        concurrentMap.put("apple", 1);
        concurrentMap.put("banana", 2);
        concurrentMap.put("cherry", 3);

        // 线程安全的遍历方式
        concurrentMap.forEach((key, value) -> {
            System.out.println("Key: " + key + ", Value: " + value);
        });
    }
}

注意:

  • ConcurrentHashMap 的迭代器具有弱一致性(Weakly Consistent),允许在迭代期间进行并发修改。
  • 避免在迭代过程中使用传统的 remove() 方法,应使用 ConcurrentHashMap 提供的 remove(key) 或 computeIfPresent() 等原子方法。

总结

Java 中 Map 集合的遍历方式丰富多样,每种方式都有其适用场景。选择合适的遍历方式不仅能提高代码的可读性,还能优化性能。以下是选择遍历方式的决策树:

  • 是否只需值?

    • 是 → 使用 values().forEach() 或 values().stream()
  • 是否需要同时访问键和值?

    • 是 → 继续。
    • 否 → 使用 keySet()
  • 是否在 Java 8+ 环境且无需删除元素?

    • 是 → 使用 forEach() + Lambda。
    • 否 → 继续。
  • 是否需要在遍历中删除元素?

    • 是 → 使用 entrySet().iterator()
    • 否 → 使用 entrySet().forEach()
  • 是否处理大数据量且在多核环境?

    • 是 → 考虑 entrySet().parallelStream()

到此这篇关于Java中Map集合遍历的多种实现方式的文章就介绍到这了,更多相关Java Map集合遍历内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 关于Java并发编程中线程间协作的两种方式

    关于Java并发编程中线程间协作的两种方式

    这篇文章主要介绍了关于Java并发编程中线程间协作的两种方式,当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源的占用权,这是消费者模式,需要的朋友可以参考下
    2023-07-07
  • Java多线程之同步工具类CyclicBarrier

    Java多线程之同步工具类CyclicBarrier

    这篇文章主要介绍Java多线程之同步工具类CyclicBarrier,它是一个同步工具类,它允许一组线程互相等待,直到达到某个公共屏障点,支持一个可选的Runnable命令,在一组线程中的最后一个线程到达之后,该命令只在每个屏障点运行一次。下面来看文章具体内容
    2021-10-10
  • Java的面向对象编程基本概念学习笔记整理

    Java的面向对象编程基本概念学习笔记整理

    这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
    2016-01-01
  • Java利用Poi读取excel并对所有类型进行处理

    Java利用Poi读取excel并对所有类型进行处理

    这篇文章主要为大家详细介绍了Java利用Poi读取excel并对所有类型进行处理的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2024-01-01
  • JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

    JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

    这篇文章主要介绍了JAVA系统中Spring Boot应用程序的配置文件application.yml的相关资料,application.yml是Spring Boot应用程序的配置文件,定义了服务器、Spring、日志、安全及其他配置属性,确保应用程序正确启动和运行,需要的朋友可以参考下
    2025-01-01
  • java使用反射访问成员变量的值示例

    java使用反射访问成员变量的值示例

    这篇文章主要介绍了java使用反射访问成员变量的值,结合实例形式分析了java基于反射机制操作类成员变量相关实现技巧,需要的朋友可以参考下
    2019-07-07
  • 一文详解Java如何实现自定义注解

    一文详解Java如何实现自定义注解

    Java实现自定义注解其实很简单,跟类定义差不多,只是属性的定义可能跟我们平时定义的属性略有不同,这篇文章主要给大家介绍了关于Java如何实现自定义注解的相关资料,需要的朋友可以参考下
    2024-07-07
  • 解决springboot报错Could not resolve placeholder‘xxx‘ in value“${XXXX}

    解决springboot报错Could not resolve placeholder‘x

    这篇文章主要介绍了解决springboot报错:Could not resolve placeholder ‘xxx‘ in value “${XXXX}问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Java细数IO流底层原理到方法使用

    Java细数IO流底层原理到方法使用

    IO主要用于设备之间的数据传输,Java将操作数据流的功能封装到了IO包中,这篇文章主要给大家介绍了关于Java新手学习之IO流简单使用的相关资料,需要的朋友可以参考下
    2022-05-05
  • Java线程池中多余的线程是如何回收的

    Java线程池中多余的线程是如何回收的

    对于经常使用第三方框架进行web开发的程序员来说,Java线程池理所应当是非常智能的,那么Java线程池中多余的线程是如何回收的?本文就来介绍一下
    2021-05-05

最新评论