前言
其实用上 JDK1.8 才是近些时日的事,毕竟没有什么新的技术点必须要去用,也懒得去换 JDK 的版本了。这几天在某论坛里看到一个有关于“HashMap如何遍历”的问题,静心一想也就知道那么一两种,于是想了想还是总结总结吧。
遍历方式
大概的总结了一下,HashMap 遍历就是分大概4个方向吧:
- 迭代器(Iterator)方式遍历。
- For Each 方式遍历。
- Lambda 表达式遍历(JDK1.8加入的)。
- Streams API 遍历(JDK1.8加入的)。
大概方向是这么几个,但是遍历方法也不少,一一举例吧。
有的朋友问我怎么我按顺序赋值怎么遍历出来顺序都不对啊,您是不是忘了一件事,Map里面是无序的O(∩_∩)O
(1)用 EntrySet 迭代器
package com.wlee.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
//定义一个Map
public static Map<String, String> testMap = new HashMap() {{
for (int i = 0; i < 10; i++) {
put("key" + i, "val" + i);
}
}};
public static void entrySetTest() {
Iterator<Map.Entry<String, String>> iterator = testMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + " ------ " + entry.getValue());
}
}
public static void main(String[] args) {
Test.entrySetTest();
}
}
(2)用 KeySet 迭代器
package com.wlee.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
//定义一个Map
public static Map<String, String> testMap = new HashMap() {{
for (int i = 0; i < 10; i++) {
put("key" + i, "val" + i);
}
}};
public static void keySetTest() {
Iterator<String> iterator = testMap.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
System.out.println(key + " ------ " + testMap.get(key));
}
}
public static void main(String[] args) {
Test.keySetTest();
}
}
(3)用 ForEach 处理 EntrySet
package com.wlee.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
//定义一个Map
public static Map<String, String> testMap = new HashMap() {{
for (int i = 0; i < 10; i++) {
put("key" + i, "val" + i);
}
}};
public static void forEach4EntrySetTest() {
for (Map.Entry<String, String> entry : testMap.entrySet()) {
System.out.println(entry.getKey() + " ------ " + entry.getValue());
}
}
public static void main(String[] args) {
Test.forEach4EntrySetTest();
}
}
(4)用 ForEach 处理 KeySet
package com.wlee.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
//定义一个Map
public static Map<String, String> testMap = new HashMap() {{
for (int i = 0; i < 10; i++) {
put("key" + i, "val" + i);
}
}};
public static void forEach4KeySetTest() {
for (String key : testMap.keySet()) {
System.out.println(key + " ------ " + testMap.get(key));
}
}
public static void main(String[] args) {
Test.forEach4KeySetTest();
}
}
(5)用 Lambda
package com.wlee.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
//定义一个Map
public static Map<String, String> testMap = new HashMap() {{
for (int i = 0; i < 10; i++) {
put("key" + i, "val" + i);
}
}};
public static void lambdaTest() {
testMap.forEach((key, value) -> {
System.out.println(key + " ------ " + value);
});
}
public static void main(String[] args) {
Test.lambdaTest();
}
}
(6)用 StreamsAPI 的单线程
package com.wlee.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
//定义一个Map
public static Map<String, String> testMap = new HashMap() {{
for (int i = 0; i < 10; i++) {
put("key" + i, "val" + i);
}
}};
public static void streamTest() {
testMap.entrySet().stream().forEach((entry) -> {
System.out.println(entry.getKey() + " ------ " + entry.getValue());
});
}
public static void main(String[] args) {
Test.streamTest();
}
}
(7)用 StreamsAPI 的多线程
package com.wlee.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
//定义一个Map
public static Map<String, String> testMap = new HashMap() {{
for (int i = 0; i < 10; i++) {
put("key" + i, "val" + i);
}
}};
public static void parallelStreamTest() {
testMap.entrySet().parallelStream().forEach((entry) -> {
System.out.println(entry.getKey() + " ------ " + entry.getValue());
});
}
public static void main(String[] args) {
Test.parallelStreamTest();
}
}
大概是总结了7种遍历方式,当然可能不一定就只有这7种,还需要朋友们多多指教。有时候您会问这些方式都哪些方式更快,性能更好。其实各种遍历方式差别不是很大。
番外篇(性能测试)
Maven 项目请先引入:
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.20</version>
<scope>provided</scope>
</dependency>
然后测试类:
package com.wlee.test;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.Throughput) //测试类型:吞吐量
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) //预热2轮,每次1s
@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) //测试5轮,每次3s
@Fork(1) //fork1个线程
@State(Scope.Thread) //每个测试线程一个实例
public class HashMapTest {
public static Map<String, String> testMap = new HashMap() {{
for (int i = 0; i < 10; i++) {
put("key" + i, "val" + i);
}
}};
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(HashMapTest.class.getSimpleName()) // 要导入的测试类
.output("d:/test/map_test.log") // 输出测试结果的文件
.build();
new Runner(opt).run(); // 执行测试
}
@Benchmark
public static void entrySetTest() {
Iterator<Map.Entry<String, String>> iterator = testMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + " ------ " + entry.getValue());
}
}
@Benchmark
public static void keySetTest() {
Iterator<String> iterator = testMap.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
System.out.println(key + " ------ " + testMap.get(key));
}
}
@Benchmark
public static void forEach4EntrySetTest() {
for (Map.Entry<String, String> entry : testMap.entrySet()) {
System.out.println(entry.getKey() + " ------ " + entry.getValue());
}
}
@Benchmark
public static void forEach4KeySetTest() {
for (String key : testMap.keySet()) {
System.out.println(key + " ------ " + testMap.get(key));
}
}
@Benchmark
public static void lambdaTest() {
testMap.forEach((key, value) -> {
System.out.println(key + " ------ " + value);
});
}
@Benchmark
public static void streamTest() {
testMap.entrySet().stream().forEach((entry) -> {
System.out.println(entry.getKey() + " ------ " + entry.getValue());
});
}
@Benchmark
public static void parallelStreamTest() {
testMap.entrySet().parallelStream().forEach((entry) -> {
System.out.println(entry.getKey() + " ------ " + entry.getValue());
});
}
}
JMH(Java Microbenchmark Harness,JAVA 微基准测试套件)是 Oracle 官方提供的性能测试工具。
测试结束从 log 日志文件中获取相关的信息片段:
Benchmark Mode Cnt Score Error Units
HashMapTest.entrySetTest thrpt 5 2.725 ± 0.319 ops/ms
HashMapTest.forEach4EntrySetTest thrpt 5 2.947 ± 0.416 ops/ms
HashMapTest.forEach4KeySetTest thrpt 5 2.914 ± 0.701 ops/ms
HashMapTest.keySetTest thrpt 5 2.799 ± 0.294 ops/ms
HashMapTest.lambdaTest thrpt 5 2.850 ± 0.455 ops/ms
HashMapTest.parallelStreamTest thrpt 5 2.420 ± 0.581 ops/ms
HashMapTest.streamTest thrpt 5 2.811 ± 0.390 ops/ms
其中 Score 列表示平均执行时间, ± 符号表示误差。从测试结果可以看出有快有慢,其实各种遍历方法在性能方面差别不是很大。
以上测试可能存在误差,毕竟每天机器的配置环境什么都不太一样,仅供参考
结语
其实以上主要是为了分享几种遍历 HashMap 的方式,具体性能测试,甚至安全测试不是主要内容。而且测试代码作者也是参考网络上的文章。