杰瑞科技汇

Java for循环遍历Map有几种方式?

在Java中,遍历Map(映射)有多种方式,尤其是在Java 8及以上版本引入了Stream API和Lambda表达式后,变得更加灵活和简洁,以下是几种常见的遍历Map的方法,从传统到现代,并附有详细说明和示例代码。

假设我们有以下一个Map作为示例:

import java.util.HashMap;
import java.util.Map;
public class MapIterationExample {
    public static void main(String[] args) {
        Map<String, Integer> studentScores = new HashMap<>();
        studentScores.put("Alice", 95);
        studentScores.put("Bob", 88);
        studentScores.put("Charlie", 76);
        studentScores.put("David", 99);
        // 我们将用不同的方法遍历这个Map
    }
}

使用 entrySet() 和 for-each 循环 (最常用、最推荐)

这是最传统也是最推荐的遍历方式,因为它同时获取键和值,效率高且代码清晰。

原理map.entrySet() 返回一个 Set<Map.Entry<K, V>>,其中每个 Map.Entry 对象代表一个键值对,然后通过for-each循环遍历这个 Set

代码示例

System.out.println("--- 方法一:使用 entrySet() 和 for-each 循环 ---");
for (Map.Entry<String, Integer> entry : studentScores.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println("学生: " + key + ", 分数: " + value);
}

优点

  • 高效:直接获取键值对,避免了通过 key 再去 map 中查找 value 的开销。
  • 代码清晰:意图明确,易于阅读和维护。
  • 兼容性好:适用于所有Java版本。

使用 keySet() 和 for-each 循环

如果你在遍历过程中只需要键,或者需要在循环中修改Map的值(但不能删除键),可以使用此方法。

原理map.keySet() 返回一个包含所有键的 Set,遍历这个 Set,然后通过 map.get(key) 来获取对应的值。

代码示例

System.out.println("\n--- 方法二:使用 keySet() 和 for-each 循环 ---");
for (String key : studentScores.keySet()) {
    Integer value = studentScores.get(key);
    System.out.println("学生: " + key + ", 分数: " + value);
}

优点

  • 当你只需要键时,非常直接。

缺点

  • 效率较低:对于每个键,都需要调用一次 map.get(key) 来获取值,这会多一次查找操作。
  • 不能在遍历时删除元素:如果你在循环中调用 map.remove(key),可能会导致 ConcurrentModificationException(并发修改异常),如果你确实需要删除,应该使用迭代器的 remove() 方法。

使用 values() 和 for-each 循环

如果你在遍历过程中只需要值,可以使用此方法。

原理map.values() 返回一个包含所有值的 Collection,直接遍历这个 Collection

代码示例

System.out.println("\n--- 方法三:使用 values() 和 for-each 循环 ---");
for (Integer value : studentScores.values()) {
    System.out.println("分数: " + value);
}

优点

  • 当你只需要值时,非常简洁。

缺点

  • 无法获取键:如果你在循环中需要同时知道键和值,此方法不适用。

使用迭代器 (Iterator)

这是在Java 5之前(以及之后)处理集合时最安全的方式,特别是当你需要在遍历过程中删除元素时。

原理: 通过 iterator() 方法获取迭代器,然后使用 while 循环和 hasNext()/next() 来遍历。

代码示例

System.out.println("\n--- 方法四:使用迭代器 (Iterator) ---");
Iterator<Map.Entry<String, Integer>> iterator = studentScores.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, Integer> entry = iterator.next();
    System.out.println("学生: " + entry.getKey() + ", 分数: " + entry.getValue());
    // 示例:在遍历时安全地删除一个元素
    if ("Bob".equals(entry.getKey())) {
        iterator.remove(); // 正确的删除方式
    }
}
System.out.println("删除Bob后的Map: " + studentScores);

优点

  • 安全删除:是遍历时删除元素的标准且安全的方式,可以避免 ConcurrentModificationException

缺点

  • 代码比for-each循环略显冗长。

使用 Java 8+ 的 forEach() 和 Lambda 表达式 (现代风格)

Java 8引入了Stream API和Lambda表达式,为集合遍历提供了更函数式、更简洁的语法。

原理Map 接口提供了一个 forEach(BiConsumer<? super K, ? super V> action) 方法,接受一个函数式接口 BiConsumer(二元消费者)作为参数,该接口接收两个参数(键和值)。

代码示例

// 重置Map以便演示
studentScores.put("Bob", 88);
System.out.println("\n--- 方法五:使用 Java 8+ 的 forEach() 和 Lambda 表达式 ---");
studentScores.forEach((key, value) -> {
    System.out.println("学生: " + key + ", 分数: " + value);
});

或者使用方法引用(如果只是简单地打印):

System.out.println("\n--- 方法五:使用 forEach() 和 方法引用 ---");
studentScores.forEach(MapIterationExample::printEntry);
// 定义一个静态方法来打印
public static void printEntry(String key, Integer value) {
    System.out.println("学生: " + key + ", 分数: " + value);
}

优点

  • 代码简洁:一行代码即可完成遍历,非常优雅。
  • 函数式风格:符合现代编程趋势。

缺点

  • 无法直接中断或删除forEach 内部的操作是“fire-and-forget”式的,你不能使用 breakcontinue,也不能在内部安全地删除元素(除非你使用外部标志和外部集合来记录要删除的项,然后在循环后删除),如果需要删除,迭代器仍然是更好的选择。

使用 Java 8+ 的 Stream API (高级用法)

Stream API提供了更强大的功能,如过滤、映射、聚合等,适用于复杂的遍历和数据处理场景。

原理: 将 Map 转换为 Stream,然后使用Stream的各种中间和终端操作。

代码示例

System.out.println("\n--- 方法六:使用 Java 8+ 的 Stream API ---");
// 1. 遍历所有键值对
System.out.println("--- 遍历所有键值对 ---");
studentScores.entrySet().stream()
             .forEach(entry -> System.out.println(entry));
// 2. 遍历并筛选分数大于90的学生
System.out.println("\n--- 筛选分数大于90的学生 ---");
studentScores.entrySet().stream()
             .filter(entry -> entry.getValue() > 90)
             .forEach(entry -> System.out.println(entry));
// 3. 遍历并只获取键
System.out.println("\n--- 只获取键 ---");
studentScores.keySet().stream()
             .forEach(System.out::println);
// 4. 遍历并只获取值
System.out.println("\n--- 只获取值 ---");
studentScores.values().stream()
             .forEach(System.out::println);

优点

  • 功能强大:可以进行链式操作,非常灵活,适合复杂的数据处理。
  • 并行处理:可以轻松地切换为并行流 .parallelStream() 来利用多核CPU提高性能。

缺点

  • 对于简单的遍历,可能有些“杀鸡用牛刀”,代码量比 forEach 多。

总结与选择建议

方法 适用场景 优点 缺点
entrySet() + for-each 通用场景,需要同时访问键和值。 高效、清晰、兼容性好。 -
keySet() + for-each 只需要键,或者需要修改值。 直接获取键。 效率较低,不能安全删除。
values() + for-each 只需要值。 简洁。 无法获取键。
Iterator 需要在遍历过程中安全地删除元素。 安全删除。 代码稍显冗长。
forEach() + Lambda 现代风格,简洁的遍历,不需要修改集合。 代码极其简洁、优雅。 无法中断或安全删除。
Stream API 需要进行复杂的链式操作(过滤、转换等)。 功能强大,支持并行。 对简单遍历可能过于复杂。

最终建议

  • 日常开发:优先选择 方法一 (entrySet() + for-each),它是最经典、最通用、最不容易出错的选择。
  • Java 8+ 项目:如果只是简单地打印或处理,且不需要修改集合,方法五 (forEach() + Lambda) 是一个非常棒的选择,代码更现代。
  • 需要删除元素:必须使用 方法四 (Iterator)
  • 复杂数据处理:使用 方法六 (Stream API)
分享:
扫描分享到社交APP
上一篇
下一篇