杰瑞科技汇

Java遍历Map的key有几种方式?

一个示例 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 的 key
    }
}

使用 keySet()for-each 循环 (最常用)

这是最传统、最直观的方法。MapkeySet() 方法会返回一个包含所有 keySet 集合,我们可以直接遍历这个 Set

代码示例:

// 使用 keySet() 和 for-each 循环
System.out.println("--- 方法一:使用 keySet() 和 for-each 循环 ---");
for (String name : studentScores.keySet()) {
    System.out.println("Key: " + name);
}

优点:

  • 代码简洁易读for-each 循环是 Java 5 引入的语法,非常直观。
  • 性能良好:对于大多数场景,性能足够好。

缺点:

  • 在遍历过程中,不能安全地修改 Map(删除 key),如果需要删除元素,应该使用 iterator.remove() 方法,而不是在 for-each 循环中直接调用 map.remove(),否则会抛出 ConcurrentModificationException 异常。

使用 entrySet()for-each 循环 (性能最佳)

虽然你的问题只要求遍历 key,但这里强烈推荐你了解这种方法,在遍历 Map 时,我们常常不仅需要 key,还需要对应的 valueentrySet() 方法返回一个包含 Map.Entry 对象的 Set,每个 Entry 对象都包含一个 key-value 对。

代码示例 (只获取 key):

// 使用 entrySet() 和 for-each 循环 (只获取 key)
System.out.println("\n--- 方法二:使用 entrySet() 和 for-each 循环 (只获取 key) ---");
for (Map.Entry<String, Integer> entry : studentScores.entrySet()) {
    String key = entry.getKey();
    // Integer value = entry.getValue(); // 如果需要 value,可以这样获取
    System.out.println("Key: " + key);
}

为什么性能最好?

  • keySet() 方法会创建一个包含所有 keySet 集合,在遍历这个 Set 时,每次通过 key 去获取 value(如果需要的话),都需要一次 HashMap.get() 操作,这是一个 O(1) 的操作,但仍有开销。
  • entrySet() 方法返回的是 Map.Entry 对象的集合,它已经将 keyvalue 绑定在一起,遍历时直接从 Entry 对象中获取 keyvalue,避免了额外的 get() 调用,因此在需要同时访问 keyvalue 时,效率最高

优点:

  • 性能最佳:当需要同时访问 keyvalue 时,避免了重复的哈希查找。
  • 代码清晰entry.getKey()entry.getValue() 的意图非常明确。

缺点:

  • 如果只需要 keyentrySet() 会比 keySet() 多创建一些 Entry 对象,内存开销略大,但在现代 JVM 和实际应用中,这个差异通常可以忽略不计。

使用 Iterator (传统且安全)

这是在 Java 5 引入 for-each 循环之前的标准方法。Iterator 提供了安全的遍历方式,尤其是在需要修改集合(如删除元素)时。

代码示例:

// 使用 Iterator 遍历 keySet
System.out.println("\n--- 方法三:使用 Iterator 遍历 keySet ---");
Iterator<String> iterator = studentScores.keySet().iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    System.out.println("Key: " + name);
    // 示例:安全地删除一个元素
    // if ("Bob".equals(name)) {
    //     iterator.remove(); // 正确的删除方式
    // }
}

优点:

  • 线程安全(在单线程修改时):可以安全地在遍历过程中使用 iterator.remove() 方法删除当前元素,而不会抛出 ConcurrentModificationException

缺点:

  • 代码冗长:相比 for-each 循环,代码更繁琐。

使用 Java 8 的 forEach() 和 Lambda 表达式 (现代且简洁)

这是 Java 8 引入的函数式编程风格,代码非常简洁优雅。

代码示例:

// 使用 Java 8 的 forEach 和 Lambda 表达式
System.out.println("\n--- 方法四:使用 Java 8 的 forEach 和 Lambda 表达式 ---");
studentScores.keySet().forEach(name -> {
    System.out.println("Key: " + name);
});
// 如果只需要 key,甚至可以这样写 (方法引用)
// System.out.println("--- 方法四 (简化版) ---");
// studentScores.keySet().forEach(System.out::println);

优点:

  • 代码极其简洁:一行代码即可完成遍历。
  • 函数式风格:更符合现代编程范式,易于与其他 Stream API 操作结合。

缺点:

  • 在遍历过程中不能修改 Map,同样会抛出 ConcurrentModificationException,如果需要修改,应该使用传统的 Iterator 方式。

使用 Java 8 的 Stream API (最灵活)

Stream API 提供了最强大的遍历和数据处理能力,支持并行操作、过滤、映射等复杂操作。

代码示例:

// 使用 Java 8 的 Stream API
System.out.println("\n--- 方法五:使用 Java 8 的 Stream API ---");
studentScores.keySet().stream()
              .filter(name -> !name.equals("Bob")) // 过滤掉 "Bob"
              .forEach(name -> System.out.println("Key: " + name));

优点:

  • 功能极其强大:可以轻松地进行链式操作,如过滤 (filter)、排序 (sorted)、映射 (map) 等。
  • 支持并行处理:只需将 .stream() 改为 .parallelStream(),即可轻松实现并行遍历,提升大数据量下的处理速度。

缺点:

  • 对于简单的遍历,代码可能显得有些“重”,不如 for-each 直接。

总结与推荐

方法 代码示例 优点 缺点 推荐场景
keySet() + for-each for (K k : map.keySet()) 简洁、易读、性能好 遍历中不能修改 Map 最常用的选择,适用于只需要 key 且不修改 Map 的场景。
entrySet() + for-each for (Map.Entry<K,V> e : map.entrySet()) 性能最佳(需同时访问key/value) 若仅需key,内存开销略大 强烈推荐,当需要同时访问 keyvalue 时的首选。
Iterator Iterator<K> it = map.keySet().iterator(); 可安全删除元素 代码冗长 需要在遍历过程中修改(删除)Map 的场景。
keySet().forEach() map.keySet().forEach(k -> ...) 极其简洁,函数式风格 遍历中不能修改 Map 追求代码简洁,且不修改 Map 的现代 Java 项目。
keySet().stream() map.keySet().stream().filter(...).forEach(...) 功能最强大,支持并行处理 对简单遍历可能过于复杂 需要对 key 进行复杂操作(如过滤、排序)或并行处理的场景。

最终建议:

  • 如果只需要 key 且不修改 Map:使用 方法一 (keySet() + for-each),它是最经典和通用的选择。
  • 如果需要同时访问 keyvalue:使用 方法二 (entrySet() + for-each),这是性能和代码清晰度的最佳平衡点。
  • 如果使用的是 Java 8+ 且追求简洁:使用 方法四 (keySet().forEach()),代码非常优雅。
  • 如果需要在遍历中删除元素:使用 方法三 (Iterator)
分享:
扫描分享到社交APP
上一篇
下一篇