一个示例 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 循环 (最常用)
这是最传统、最直观的方法。Map 的 keySet() 方法会返回一个包含所有 key 的 Set 集合,我们可以直接遍历这个 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,还需要对应的 value。entrySet() 方法返回一个包含 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()方法会创建一个包含所有key的Set集合,在遍历这个Set时,每次通过key去获取value(如果需要的话),都需要一次HashMap.get()操作,这是一个 O(1) 的操作,但仍有开销。entrySet()方法返回的是Map.Entry对象的集合,它已经将key和value绑定在一起,遍历时直接从Entry对象中获取key和value,避免了额外的get()调用,因此在需要同时访问key和value时,效率最高。
优点:
- 性能最佳:当需要同时访问
key和value时,避免了重复的哈希查找。 - 代码清晰:
entry.getKey()和entry.getValue()的意图非常明确。
缺点:
- 如果只需要
key,entrySet()会比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,内存开销略大 | 强烈推荐,当需要同时访问 key 和 value 时的首选。 |
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),它是最经典和通用的选择。 - 如果需要同时访问
key和value:使用 方法二 (entrySet()+for-each),这是性能和代码清晰度的最佳平衡点。 - 如果使用的是 Java 8+ 且追求简洁:使用 方法四 (
keySet().forEach()),代码非常优雅。 - 如果需要在遍历中删除元素:使用 方法三 (
Iterator)。
