使用 keySet() 遍历键
for (K key : map.keySet()) {
V value = map.get(key);
}
- 优点:简单直观。
- 缺点:在每次循环时调用 map.get(key),这意味着对于每个键都会执行一次哈希查找,性能稍差。
使用 entrySet() 遍历键值对(Iterator的方式)
for (Map.Entry<K, V> entry : map.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
}
- 优点:效率较高。因为 entrySet() 直接返回 Map.Entry,同时获取键和值,不需要再次调用 get()。
- 缺点:无显著缺点。
使用 forEach 方法(Java 8 及之后)
map.forEach((key, value) -> {
// 操作键和值
});
- 优点:简洁且高效,利用内部优化。
- 缺点:可读性可能对部分开发者不如传统遍历方式。
使用 Stream API 遍历
map.entrySet().stream().forEach(entry -> {
K key = entry.getKey();
V value = entry.getValue();
});
优点:
- List item
- 可以利用并行流(parallelStream())进行并行处理,适合大规模数据处理。
- 语法简洁优雅,符合函数式编程风格。
- 缺点:
- 在小规模数据或简单场景下,Stream 的开销可能稍大,尤其是在引入并行流时,可能增加线程管理开销。
- 相比于直接的 for-each 循环,在某些场景下 Stream 可能会稍慢,因为它会引入额外的中间操作步骤。
性能
如果考虑到普通数据规模和常见遍历场景,性能通常是以下顺序:
- entrySet()(传统遍历,键值同时获取)
- forEach()(简洁且性能接近 entrySet())
- Stream(在并行流中可能更快,普通场景下稍慢)
- keySet()(需要额外的 get() 操作)
Stream 更适合大规模并行处理场景,而在小规模数据或常规使用场景下,entrySet()方式通常是最快的选择。
以下是通过代码进行对比的结果
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class HashMapIteration {
private static final int ITERATIONS = 100000; // 遍历的次数
private static final int MAP_SIZE = 10000; // HashMap 中的元素数量
public static void main(String[] args) {
// 初始化 HashMap
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < MAP_SIZE; i++) {
map.put(i, i);
}
// 测试 keySet + get() 的时间
long startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
for (Integer key : map.keySet()) {
Integer value = map.get(key);
}
}
long endTime = System.nanoTime();
System.out.println("Method 1 (keySet + get) Time: " + (endTime - startTime) / 1000000 + " ms");
// 测试 entrySet 的时间
startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
Integer key = entry.getKey();
Integer value = entry.getValue();
}
}
endTime = System.nanoTime();
System.out.println("Method 2 (entrySet) Time: " + (endTime - startTime) / 1000000 + " ms");
// 测试 forEach 的时间
startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
map.forEach((key, value) -> {
// 遍历键值对
});
}
endTime = System.nanoTime();
System.out.println("Method 3 (forEach) Time: " + (endTime - startTime) / 1000000 + " ms");
// 测试 Stream API 的时间
startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
map.entrySet().stream().forEach(entry -> {
Integer key = entry.getKey();
Integer value = entry.getValue();
});
}
endTime = System.nanoTime();
System.out.println("Method 4 (Stream) Time: " + (endTime - startTime) / 1000000 + " ms");
// 测试使用 Iterator 的时间
startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, Integer> entry = iterator.next();
Integer key = entry.getKey();
Integer value = entry.getValue();
}
}
endTime = System.nanoTime();
System.out.println("Method 5 (Iterator) Time: " + (endTime - startTime) / 1000000 + " ms");
}
}
Method 1 (keySet + get) Time: 6883 ms
Method 2 (entrySet) Time: 4670 ms
Method 3 (forEach) Time: 2699 ms
Method 4 (Stream) Time: 4199 ms
Method 5 (Iterator) Time: 4609 ms