前言
其實用上 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 的方式,具體性能測試,甚至安全測試不是主要內容。而且測試代碼作者也是參考網絡上的文章。
作者:WorkerLee
鏈接:https://juejin.im/post/5eea2040f265da02a224818f