前言
HashMap 遍历从大的方向来说,可分为以下 4 类:
迭代器(Iterator)方式遍历;
For Each 方式遍历;
Lambda 表达式遍历(JDK 1.8+);
Streams API 遍历(JDK 1.8+)。
但每种类型下又有不同的实现方式
,因此具体的遍历方式
又可以分为以下 7 种:
使用迭代器(Iterator)EntrySet 的方式进行遍历;
使用迭代器(Iterator)KeySet 的方式进行遍历;
使用 For Each EntrySet 的方式进行遍历;
使用 For Each KeySet 的方式进行遍历;
使用 Lambda 表达式的方式进行遍历;
使用 Streams API 单线程的方式进行遍历;
使用 Streams API 多线程的方式进行遍历。
不同的实现方式
public class HashMapTest {
public static void main(String[] args) {
// 创建并赋值 HashMap
Map<Integer, String> map = new HashMap();
map.put(1, "Java");
map.put(2, "JDK");
map.put(3, "Spring Framework");
map.put(4, "MyBatis framework");
map.put(5, "Java中文社群");
// 遍历
}
}
迭代器 EntrySet
Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> entry = iterator.next();
System.out.print(entry.getKey());
System.out.print(entry.getValue());
}
迭代器 KeySet
Iterator<Integer> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
Integer key = iterator.next();
System.out.print(key);
System.out.print(map.get(key));
}
ForEach EntrySet
for (Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.print(entry.getKey());
System.out.print(entry.getValue());
}
ForEach KeySet
for (Integer key : map.keySet()) {
System.out.print(key);
System.out.print(map.get(key));
}
Lambda
map.forEach((key, value) -> {
System.out.print(key);
System.out.print(value);
});
Streams API 单线程
map.entrySet().stream().forEach((entry) -> {
System.out.print(entry.getKey());
System.out.print(entry.getValue());
});
Streams API 多线程
map.entrySet().parallelStream().forEach((entry) -> {
System.out.print(entry.getKey());
System.out.print(entry.getValue());
});
性能测试
性能原理分析
通过 javac
,编译成字节码
来看具体的原因
迭代器循环
和 for 循环
的遍历的 EntrySet
最终生成的代码是一样的
,他们都是在循环中
创建了一个遍历对象 Entry
public static void entrySet() {
Iterator var0 = map.entrySet().iterator();
while(var0.hasNext()) {
Entry var1 = (Entry)var0.next();
System.out.println(var1.getKey());
System.out.println((String)var1.getValue());
}
}
public static void forEachEntrySet() {
Iterator var0 = map.entrySet().iterator();
while(var0.hasNext()) {
Entry var1 = (Entry)var0.next();
System.out.println(var1.getKey());
System.out.println((String)var1.getValue());
}
}
迭代器
和 for 循环遍历
的 KeySet
代码也是一样的
public static void keySet() {
Iterator var0 = map.keySet().iterator();
while(var0.hasNext()) {
Integer var1 = (Integer)var0.next();
System.out.println(var1);
System.out.println((String)map.get(var1));
}
}
public static void forEachKeySet() {
Iterator var0 = map.keySet().iterator();
while(var0.hasNext()) {
Integer var1 = (Integer)var0.next();
System.out.println(var1);
System.out.println((String)map.get(var1));
}
}
KeySet
在循环中创建了一个 Integer 的局部变量
,并且值是从 map 对象中直接获取
总结
从性能的角度来说 EntrySet 和 KeySet 几乎是相近的
从代码的优雅型和可读性来说,还是推荐使用 EntrySet
安全性测试
迭代器方式 -删除数据安全
Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> entry = iterator.next();
if (entry.getKey() == 1) {
// 删除
System.out.println("del:" + entry.getKey());
iterator.remove();
} else {
System.out.println("show:" + entry.getKey());
}
}
show:0
del:1
show:2
测试结果:迭代器中循环删除数据安全
For 循环方式 -删除数据非安全
for (Map.Entry<Integer, String> entry : map.entrySet()) {
if (entry.getKey() == 1) {
// 删除
System.out.println("del:" + entry.getKey());
map.remove(entry.getKey());
} else {
System.out.println("show:" + entry.getKey());
}
}
测试结果:For 循环中删除数据非安全
Lambda 方式 -删除数据非安全
map.forEach((key, value) -> {
if (key == 1) {
System.out.println("del:" + key);
map.remove(key);
} else {
System.out.println("show:" + key);
}
});
测试结果:Lambda 循环中删除数据非安全
Lambda 删除的正确方式
// 根据 map 中的 key 去判断删除
map.keySet().removeIf(key -> key == 1);
map.forEach((key, value) -> {
System.out.println("show:" + key);
});
先使用 Lambda 的 removeIf 删除多余的数据
,再进行循环
是一种正确操作集合的方式。
Stream 方式 - 删除数据非安全
map.entrySet().stream().forEach((entry) -> {
if (entry.getKey() == 1) {
System.out.println("del:" + entry.getKey());
map.remove(entry.getKey());
} else {
System.out.println("show:" + entry.getKey());
}
});
测试结果:Stream 循环中删除数据非安全
Stream 循环的正确方式
map.entrySet().stream().filter(m -> 1 != m.getKey()).forEach((entry) -> {
if (entry.getKey() == 1) {
System.out.println("del:" + entry.getKey());
} else {
System.out.println("show:" + entry.getKey());
}
});
使用 Stream
中的 filter 过滤掉无用
的数据,再进行遍历
也是一种安全的操作集合的方式
总结
不能 在遍历中使用集合 map.remove() 来删除数据,这是非安全的操作方式
可以使用迭代器的 iterator.remove() 的方法来删除数据,这是安全的删除集合的方式
可以使用 Lambda 中的 removeIf 来提前删除数据
可以使用 Stream 中的 filter 过滤掉要删除的数据进行循环
可以在 for 循环前删除数据在遍历也是线程安全的