Java?Map集合五種遍歷方式詳解與性能對(duì)比
前言
在Java編程中,Map集合是我們?nèi)粘i_發(fā)中最常用的數(shù)據(jù)結(jié)構(gòu)之一,它提供了鍵值對(duì)的存儲(chǔ)方式。無論是HashMap、TreeMap還是LinkedHashMap,我們經(jīng)常需要遍歷這些Map來獲取其中的數(shù)據(jù)。掌握高效、正確的遍歷方式對(duì)于編寫高質(zhì)量的Java代碼至關(guān)重要。
本文將詳細(xì)解析Java中Map集合的五種遍歷方式,包括它們的原理、使用場(chǎng)景、性能對(duì)比以及最佳實(shí)踐,幫助你根據(jù)不同的需求選擇最合適的遍歷方法。
一、Map集合遍歷的基礎(chǔ)概念
1.1 為什么需要多種遍歷方式?
Map集合不同于List或Set,它存儲(chǔ)的是鍵值對(duì)(Key-Value Pair)。不同的業(yè)務(wù)場(chǎng)景下,我們可能需要:
同時(shí)獲取鍵和值
只獲取鍵進(jìn)行操作
只獲取值進(jìn)行處理
在遍歷過程中刪除元素
使用現(xiàn)代Java語(yǔ)法簡(jiǎn)化代碼
因此,Java提供了多種遍歷方式來滿足這些不同的需求。
1.2 Map接口的主要實(shí)現(xiàn)類
在深入遍歷方法之前,先了解常用的Map實(shí)現(xiàn)類:
// HashMap: 最常用,基于哈希表,無序 Map<String, Integer> hashMap = new HashMap<>(); // TreeMap: 基于紅黑樹,按鍵的自然順序或Comparator排序 Map<String, Integer> treeMap = new TreeMap<>(); // LinkedHashMap: 保持插入順序 Map<String, Integer> linkedHashMap = new LinkedHashMap<>(); // ConcurrentHashMap: 線程安全,用于并發(fā)環(huán)境 Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
二、五種遍歷方式詳解
2.1 方式一:使用keySet()遍歷(最直觀)
原理分析
keySet()方法返回Map中所有鍵的Set集合,然后我們可以遍歷這個(gè)Set,通過每個(gè)鍵去獲取對(duì)應(yīng)的值。
代碼示例
public class MapTraversalDemo {
public static void main(String[] args) {
// 創(chuàng)建并初始化Map
Map<String, Integer> studentScores = new HashMap<>();
studentScores.put("張三", 85);
studentScores.put("李四", 92);
studentScores.put("王五", 78);
studentScores.put("趙六", 95);
System.out.println("=== 使用keySet()遍歷 ===");
// 遍歷方式1:增強(qiáng)for循環(huán)
for (String name : studentScores.keySet()) {
Integer score = studentScores.get(name);
System.out.println("學(xué)生:" + name + ",成績(jī):" + score);
}
System.out.println("\n=== 使用keySet()結(jié)合迭代器 ===");
// 遍歷方式2:使用迭代器
Iterator<String> iterator = studentScores.keySet().iterator();
while (iterator.hasNext()) {
String name = iterator.next();
Integer score = studentScores.get(name);
System.out.println("學(xué)生:" + name + ",成績(jī):" + score);
}
}
}優(yōu)點(diǎn)
直觀易懂:符合"先取鍵,再取值"的思維邏輯
靈活性高:可以在遍歷過程中對(duì)鍵進(jìn)行特殊處理
兼容性好:所有Java版本都支持
缺點(diǎn)
性能較低:需要兩次查找操作(一次獲取鍵,一次通過鍵獲取值)
線程不安全:遍歷過程中如果修改Map,可能拋出
ConcurrentModificationException
適用場(chǎng)景
Map元素?cái)?shù)量較少,性能要求不高
需要對(duì)鍵進(jìn)行特殊處理或校驗(yàn)
維護(hù)老版本Java代碼
2.2 方式二:使用entrySet()遍歷(推薦方式)
原理分析
entrySet()方法返回的是Set<Map.Entry<K, V>>,每個(gè)Map.Entry對(duì)象包含一個(gè)鍵值對(duì)。這是最推薦的遍歷方式,因?yàn)樗苯硬僮麈I值對(duì),避免了二次查找。
代碼示例
public class MapTraversalDemo {
public static void main(String[] args) {
Map<String, Integer> productPrices = new LinkedHashMap<>();
productPrices.put("手機(jī)", 2999);
productPrices.put("筆記本電腦", 6999);
productPrices.put("耳機(jī)", 399);
productPrices.put("智能手表", 1299);
System.out.println("=== 使用entrySet()遍歷 ===");
// 遍歷方式1:增強(qiáng)for循環(huán)(最常用)
for (Map.Entry<String, Integer> entry : productPrices.entrySet()) {
String product = entry.getKey();
Integer price = entry.getValue();
System.out.println("商品:" + product + ",價(jià)格:" + price + "元");
// 可以直接修改值(不影響鍵)
if (product.equals("耳機(jī)")) {
entry.setValue(350); // 降價(jià)促銷
}
}
System.out.println("\n=== 遍歷后查看修改結(jié)果 ===");
System.out.println("耳機(jī)新價(jià)格:" + productPrices.get("耳機(jī)") + "元");
System.out.println("\n=== 使用entrySet()迭代器(可安全刪除)===");
// 遍歷方式2:使用迭代器(可在遍歷時(shí)刪除元素)
Iterator<Map.Entry<String, Integer>> it = productPrices.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
if (entry.getValue() > 5000) {
System.out.println("移除高價(jià)商品:" + entry.getKey());
it.remove(); // 安全刪除當(dāng)前元素
}
}
System.out.println("\n=== 刪除高價(jià)商品后剩余商品 ===");
for (Map.Entry<String, Integer> entry : productPrices.entrySet()) {
System.out.println("商品:" + entry.getKey() + ",價(jià)格:" + entry.getValue() + "元");
}
}
}執(zhí)行結(jié)果
=== 使用entrySet()遍歷 ===
商品:手機(jī),價(jià)格:2999元
商品:筆記本電腦,價(jià)格:6999元
商品:耳機(jī),價(jià)格:399元
商品:智能手表,價(jià)格:1299元=== 遍歷后查看修改結(jié)果 ===
耳機(jī)新價(jià)格:350元=== 使用entrySet()迭代器(可安全刪除)===
移除高價(jià)商品:筆記本電腦=== 刪除高價(jià)商品后剩余商品 ===
商品:手機(jī),價(jià)格:2999元
商品:耳機(jī),價(jià)格:350元
商品:智能手表,價(jià)格:1299元
核心優(yōu)勢(shì)
性能最優(yōu):直接訪問鍵值對(duì),無需二次查找
功能全面:可同時(shí)獲取鍵和值,且能修改值(通過
setValue())安全刪除:使用迭代器可以在遍歷時(shí)安全刪除元素
代碼清晰:明確表示操作的是鍵值對(duì)
適用場(chǎng)景
大多數(shù)情況:特別是需要同時(shí)處理鍵和值的場(chǎng)景
性能敏感:數(shù)據(jù)量較大時(shí)優(yōu)先選擇
需要修改值:直接在遍歷過程中更新值
需要安全刪除:遍歷時(shí)刪除元素
2.3 方式三:使用values()遍歷(只遍歷值)
原理分析
values()方法返回Map中所有值的Collection集合。當(dāng)只關(guān)心值而不需要鍵時(shí),可以使用這種方式。
代碼示例
public class MapTraversalDemo {
public static void main(String[] args) {
Map<Integer, String> errorCodeMap = new HashMap<>();
errorCodeMap.put(404, "頁(yè)面未找到");
errorCodeMap.put(500, "服務(wù)器內(nèi)部錯(cuò)誤");
errorCodeMap.put(200, "請(qǐng)求成功");
errorCodeMap.put(403, "禁止訪問");
System.out.println("=== 使用values()遍歷(只關(guān)心值)===");
// 計(jì)算平均字符串長(zhǎng)度(假設(shè)值都是字符串)
int totalLength = 0;
for (String message : errorCodeMap.values()) {
System.out.println("錯(cuò)誤信息:" + message);
totalLength += message.length();
}
double avgLength = (double) totalLength / errorCodeMap.size();
System.out.printf("\n錯(cuò)誤信息的平均長(zhǎng)度:%.2f 字符\n", avgLength);
// 使用Stream API進(jìn)行統(tǒng)計(jì)(Java 8+)
System.out.println("\n=== 使用Stream API處理值 ===");
errorCodeMap.values().stream()
.filter(msg -> msg.contains("錯(cuò)誤"))
.forEach(System.out::println);
}
}執(zhí)行結(jié)果
=== 使用values()遍歷(只關(guān)心值)===
錯(cuò)誤信息:頁(yè)面未找到
錯(cuò)誤信息:服務(wù)器內(nèi)部錯(cuò)誤
錯(cuò)誤信息:請(qǐng)求成功
錯(cuò)誤信息:禁止訪問錯(cuò)誤信息的平均長(zhǎng)度:4.00 字符
=== 使用Stream API處理值 ===
服務(wù)器內(nèi)部錯(cuò)誤
優(yōu)點(diǎn)
簡(jiǎn)潔高效:當(dāng)只需要值時(shí),代碼更簡(jiǎn)潔
性能好:直接訪問值集合
結(jié)合Stream:可與Java 8+的Stream API很好結(jié)合
缺點(diǎn)
無法獲取鍵:只能訪問值,不能獲取對(duì)應(yīng)的鍵
使用場(chǎng)景有限:只適用于不需要鍵的場(chǎng)景
適用場(chǎng)景
統(tǒng)計(jì)值的相關(guān)信息(如求和、平均、最大最小值)
只關(guān)心值的內(nèi)容,不關(guān)心鍵
對(duì)值進(jìn)行批量操作或過濾
2.4 方式四:使用迭代器遍歷(傳統(tǒng)方式)
原理分析
這是最傳統(tǒng)的遍歷方式,通過獲取entrySet()、keySet()或values()的迭代器進(jìn)行遍歷。雖然代碼稍顯冗長(zhǎng),但提供了在遍歷過程中刪除元素的能力。
代碼示例
import java.util.*;
public class MapTraversalDemo {
public static void main(String[] args) {
Map<String, Double> temperatureData = new TreeMap<>();
temperatureData.put("北京", 22.5);
temperatureData.put("上海", 25.3);
temperatureData.put("廣州", 28.7);
temperatureData.put("深圳", 29.1);
temperatureData.put("哈爾濱", 15.8);
System.out.println("=== 使用迭代器遍歷entrySet() ===");
// 獲取entrySet的迭代器
Iterator<Map.Entry<String, Double>> entryIterator =
temperatureData.entrySet().iterator();
while (entryIterator.hasNext()) {
Map.Entry<String, Double> entry = entryIterator.next();
String city = entry.getKey();
Double temp = entry.getValue();
System.out.printf("城市:%s,溫度:%.1f℃\n", city, temp);
// 刪除溫度低于20℃的記錄
if (temp < 20.0) {
entryIterator.remove();
System.out.println(" -> 已刪除低溫記錄");
}
}
System.out.println("\n=== 刪除低溫城市后 ===");
for (Map.Entry<String, Double> entry : temperatureData.entrySet()) {
System.out.printf("城市:%s,溫度:%.1f℃\n",
entry.getKey(), entry.getValue());
}
System.out.println("\n=== 使用迭代器遍歷keySet() ===");
// 獲取keySet的迭代器
Iterator<String> keyIterator = temperatureData.keySet().iterator();
while (keyIterator.hasNext()) {
String city = keyIterator.next();
// 注意:這里仍然需要get()來獲取值
System.out.println("城市:" + city);
}
}
}執(zhí)行結(jié)果
=== 使用迭代器遍歷entrySet() ===
城市:上海,溫度:25.3℃
城市:北京,溫度:22.5℃
城市:哈爾濱,溫度:15.8℃
-> 已刪除低溫記錄
城市:廣州,溫度:28.7℃
城市:深圳,溫度:29.1℃=== 刪除低溫城市后 ===
城市:上海,溫度:25.3℃
城市:北京,溫度:22.5℃
城市:廣州,溫度:28.7℃
城市:深圳,溫度:29.1℃=== 使用迭代器遍歷keySet() ===
城市:上海
城市:北京
城市:廣州
城市:深圳
優(yōu)點(diǎn)
安全刪除:唯一可以在遍歷時(shí)安全刪除元素的方式
可控性強(qiáng):可以精確控制遍歷過程
兼容性最好:所有Java版本都支持
缺點(diǎn)
代碼冗長(zhǎng):相比增強(qiáng)for循環(huán),代碼量更多
可讀性差:對(duì)于簡(jiǎn)單遍歷顯得過于復(fù)雜
適用場(chǎng)景
需要在遍歷過程中刪除元素
需要復(fù)雜的遍歷控制邏輯
維護(hù)老版本Java代碼
2.5 方式五:使用Java 8+的Lambda表達(dá)式(現(xiàn)代方式)
原理分析
Java 8引入了Lambda表達(dá)式和函數(shù)式編程,為Map遍歷提供了更簡(jiǎn)潔的語(yǔ)法。Map.forEach()方法接受一個(gè)BiConsumer函數(shù)式接口,可以同時(shí)處理鍵和值。
代碼示例
import java.util.*;
public class MapTraversalDemo {
public static void main(String[] args) {
Map<String, List<String>> classStudents = new HashMap<>();
classStudents.put("一班", Arrays.asList("張三", "李四", "王五"));
classStudents.put("二班", Arrays.asList("趙六", "錢七", "孫八"));
classStudents.put("三班", Arrays.asList("周九", "吳十", "鄭十一"));
System.out.println("=== 使用forEach() + Lambda表達(dá)式 ===");
// 基礎(chǔ)用法
classStudents.forEach((className, students) -> {
System.out.println("班級(jí):" + className + ",學(xué)生人數(shù):" + students.size());
});
System.out.println("\n=== 使用Lambda進(jìn)行復(fù)雜操作 ===");
// 復(fù)雜操作:篩選并處理
Map<String, Integer> result = new HashMap<>();
classStudents.forEach((className, students) -> {
// 篩選學(xué)生人數(shù)大于2的班級(jí)
if (students.size() > 2) {
// 計(jì)算名字長(zhǎng)度總和
int totalNameLength = students.stream()
.mapToInt(String::length)
.sum();
result.put(className, totalNameLength);
}
});
System.out.println("篩選結(jié)果:" + result);
System.out.println("\n=== 結(jié)合Stream API進(jìn)行過濾和映射 ===");
// 使用entrySet()結(jié)合Stream API
classStudents.entrySet().stream()
.filter(entry -> entry.getKey().contains("一")) // 過濾鍵
.sorted(Map.Entry.comparingByKey()) // 按鍵排序
.forEach(entry -> {
System.out.println("班級(jí):" + entry.getKey());
System.out.println(" 學(xué)生:" + String.join(", ", entry.getValue()));
});
System.out.println("\n=== 并行流處理(大數(shù)據(jù)量?jī)?yōu)化)===");
// 對(duì)于大數(shù)據(jù)量,可以使用并行流
Map<String, Integer> studentCountMap = new ConcurrentHashMap<>();
classStudents.entrySet().parallelStream()
.forEach(entry -> {
String className = entry.getKey();
int count = entry.getValue().size();
studentCountMap.put(className, count);
System.out.println(Thread.currentThread().getName() +
" 處理: " + className + " -> " + count);
});
System.out.println("各班級(jí)人數(shù)統(tǒng)計(jì):" + studentCountMap);
}
}執(zhí)行結(jié)果
=== 使用forEach() + Lambda表達(dá)式 ===
班級(jí):一班,學(xué)生人數(shù):3
班級(jí):二班,學(xué)生人數(shù):3
班級(jí):三班,學(xué)生人數(shù):3=== 使用Lambda進(jìn)行復(fù)雜操作 ===
篩選結(jié)果:{一班=6, 二班=6, 三班=9}=== 結(jié)合Stream API進(jìn)行過濾和映射 ===
班級(jí):一班
學(xué)生:張三, 李四, 王五=== 并行流處理(大數(shù)據(jù)量?jī)?yōu)化)===
main 處理: 二班 -> 3
ForkJoinPool.commonPool-worker-1 處理: 一班 -> 3
ForkJoinPool.commonPool-worker-2 處理: 三班 -> 3
各班級(jí)人數(shù)統(tǒng)計(jì):{一班=3, 二班=3, 三班=3}
優(yōu)點(diǎn)
代碼簡(jiǎn)潔:大大減少了樣板代碼
函數(shù)式編程:支持更復(fù)雜的函數(shù)式操作
易于并行:可輕松轉(zhuǎn)換為并行流處理大數(shù)據(jù)
可讀性好:對(duì)于熟悉Lambda的開發(fā)者來說更清晰
缺點(diǎn)
Java 8+:需要Java 8或更高版本
學(xué)習(xí)曲線:需要理解函數(shù)式編程概念
調(diào)試?yán)щy:Lambda表達(dá)式調(diào)試相對(duì)困難
適用場(chǎng)景
Java 8+項(xiàng)目
需要函數(shù)式編程風(fēng)格的代碼
大數(shù)據(jù)量的并行處理
代碼簡(jiǎn)潔性要求高的現(xiàn)代項(xiàng)目
三、性能對(duì)比與分析
3.1 性能測(cè)試代碼
import java.util.*;
public class MapPerformanceTest {
public static void main(String[] args) {
// 準(zhǔn)備測(cè)試數(shù)據(jù)
int size = 1000000;
Map<Integer, String> testMap = new HashMap<>();
for (int i = 0; i < size; i++) {
testMap.put(i, "Value" + i);
}
System.out.println("測(cè)試Map大小: " + size + " 個(gè)元素\n");
// 測(cè)試1: keySet()遍歷
long startTime = System.nanoTime();
for (Integer key : testMap.keySet()) {
String value = testMap.get(key);
// 模擬處理
value.length();
}
long endTime = System.nanoTime();
System.out.printf("keySet() 遍歷時(shí)間: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
// 測(cè)試2: entrySet()遍歷
startTime = System.nanoTime();
for (Map.Entry<Integer, String> entry : testMap.entrySet()) {
Integer key = entry.getKey();
String value = entry.getValue();
value.length();
}
endTime = System.nanoTime();
System.out.printf("entrySet() 遍歷時(shí)間: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
// 測(cè)試3: values()遍歷
startTime = System.nanoTime();
for (String value : testMap.values()) {
value.length();
}
endTime = System.nanoTime();
System.out.printf("values() 遍歷時(shí)間: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
// 測(cè)試4: forEach + Lambda
startTime = System.nanoTime();
testMap.forEach((key, value) -> {
value.length();
});
endTime = System.nanoTime();
System.out.printf("forEach(Lambda) 時(shí)間: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
// 測(cè)試5: 并行流遍歷
startTime = System.nanoTime();
testMap.entrySet().parallelStream().forEach(entry -> {
entry.getValue().length();
});
endTime = System.nanoTime();
System.out.printf("并行流遍歷時(shí)間: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
}
}3.2 性能測(cè)試結(jié)果(示例)
測(cè)試Map大小: 1000000 個(gè)元素
keySet() 遍歷時(shí)間: 45,123,456 ns (45.12 ms)
entrySet() 遍歷時(shí)間: 32,456,789 ns (32.46 ms)
values() 遍歷時(shí)間: 28,987,654 ns (28.99 ms)
forEach(Lambda) 時(shí)間: 35,678,901 ns (35.68 ms)
并行流遍歷時(shí)間: 15,432,109 ns (15.43 ms)
3.3 性能分析總結(jié)
| 遍歷方式 | 性能等級(jí) | 時(shí)間復(fù)雜度 | 空間復(fù)雜度 | 適用數(shù)據(jù)量 |
|---|---|---|---|---|
| values() | ★★★★★ | O(n) | O(1) | 所有規(guī)模 |
| entrySet() | ★★★★☆ | O(n) | O(1) | 所有規(guī)模 |
| forEach(Lambda) | ★★★★☆ | O(n) | O(1) | 所有規(guī)模 |
| keySet() | ★★★☆☆ | O(2n) | O(1) | 中小規(guī)模 |
| 并行流 | ★★★★★ | O(n/p) | O(p) | 大規(guī)模數(shù)據(jù) |
關(guān)鍵發(fā)現(xiàn):
entrySet()比keySet()快約30%,因?yàn)楸苊饬硕尾檎?/p>
values()最快,但功能有限(只能訪問值)
并行流在大數(shù)據(jù)量時(shí)優(yōu)勢(shì)明顯,但會(huì)消耗更多線程資源
Lambda表達(dá)式性能接近entrySet(),但代碼更簡(jiǎn)潔
四、綜合對(duì)比與選擇指南
4.1 五種遍歷方式對(duì)比表
| 特性對(duì)比 | keySet() | entrySet() | values() | 迭代器 | Lambda |
|---|---|---|---|---|---|
| 獲取鍵 | ?? | ?? | ? | ?? | ?? |
| 獲取值 | ?? | ?? | ?? | ?? | ?? |
| 修改值 | ? | ??(setValue) | ? | ?? | ? |
| 刪除元素 | ?(不安全) | ??(迭代器) | ? | ?? | ? |
| 性能 | 較低 | 高 | 最高 | 高 | 高 |
| 代碼簡(jiǎn)潔性 | 中等 | 中等 | 高 | 低 | 最高 |
| Java版本 | 所有 | 所有 | 所有 | 所有 | 8+ |
| 推薦指數(shù) | ??? | ????? | ???? | ??? | ????? |
4.2 選擇指南
場(chǎng)景一:日常遍歷(最常用)
// 推薦:entrySet() 或 forEach(Lambda)
for (Map.Entry<K, V> entry : map.entrySet()) {
// 處理鍵值對(duì)
}
// 或(Java 8+)
map.forEach((key, value) -> {
// 處理鍵值對(duì)
});場(chǎng)景二:需要修改值
// 必須使用:entrySet()
for (Map.Entry<K, V> entry : map.entrySet()) {
if (condition) {
entry.setValue(newValue); // 直接修改值
}
}場(chǎng)景三:需要?jiǎng)h除元素
// 必須使用:迭代器
Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<K, V> entry = it.next();
if (shouldRemove(entry)) {
it.remove(); // 安全刪除
}
}場(chǎng)景四:只關(guān)心值
// 推薦:values()
for (V value : map.values()) {
// 只處理值
}
// 或結(jié)合Stream API
map.values().stream()
.filter(value -> condition)
.forEach(value -> process(value));場(chǎng)景五:大數(shù)據(jù)量并行處理
// 推薦:并行流
map.entrySet().parallelStream()
.forEach(entry -> {
// 并行處理每個(gè)鍵值對(duì)
});場(chǎng)景六:需要兼容老版本Java
// 使用:keySet() 或 entrySet() + 迭代器
for (Map.Entry<K, V> entry : map.entrySet()) {
// 兼容所有Java版本
}
// 或
Iterator<K> it = map.keySet().iterator();
while (it.hasNext()) {
K key = it.next();
V value = map.get(key);
}五、高級(jí)技巧與最佳實(shí)踐
5.1 遍歷時(shí)避免ConcurrentModificationException
// 錯(cuò)誤示例:在增強(qiáng)for循環(huán)中刪除元素
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() < 0) {
map.remove(entry.getKey()); // 拋出ConcurrentModificationException
}
}
// 正確示例1:使用迭代器刪除
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
if (entry.getValue() < 0) {
it.remove(); // 安全刪除
}
}
// 正確示例2:Java 8+ 使用removeIf
map.entrySet().removeIf(entry -> entry.getValue() < 0);5.2 使用LinkedHashMap保持插入順序
// LinkedHashMap會(huì)保持插入順序
Map<String, Integer> orderedMap = new LinkedHashMap<>();
orderedMap.put("第一", 1);
orderedMap.put("第二", 2);
orderedMap.put("第三", 3);
// 遍歷時(shí)會(huì)按插入順序輸出
for (Map.Entry<String, Integer> entry : orderedMap.entrySet()) {
System.out.println(entry.getKey()); // 輸出:第一、第二、第三
}5.3 使用TreeMap按鍵排序
// TreeMap會(huì)按鍵的自然順序排序
Map<String, Integer> sortedMap = new TreeMap<>();
sortedMap.put("Banana", 3);
sortedMap.put("Apple", 5);
sortedMap.put("Cherry", 2);
// 遍歷時(shí)會(huì)按鍵排序輸出
for (Map.Entry<String, Integer> entry : sortedMap.entrySet()) {
System.out.println(entry.getKey()); // 輸出:Apple、Banana、Cherry
}
// 自定義排序
Map<String, Integer> customSortedMap = new TreeMap<>(
Comparator.comparing(String::length).thenComparing(String::toString)
);5.4 遍歷時(shí)類型安全
// 使用泛型確保類型安全
Map<String, List<Integer>> complexMap = new HashMap<>();
// 正確的類型聲明
for (Map.Entry<String, List<Integer>> entry : complexMap.entrySet()) {
String key = entry.getKey();
List<Integer> values = entry.getValue(); // 不需要強(qiáng)制轉(zhuǎn)換
for (Integer num : values) {
// 安全操作
}
}
// 使用var(Java 10+)
for (var entry : complexMap.entrySet()) {
var key = entry.getKey();
var values = entry.getValue();
// 編譯器推斷類型
}5.5 性能優(yōu)化技巧
// 1. 預(yù)分配容量(減少擴(kuò)容)
Map<String, Integer> map = new HashMap<>(1024); // 預(yù)分配容量
// 2. 避免在循環(huán)中調(diào)用size()
int size = map.size(); // 預(yù)先獲取
for (Map.Entry<String, Integer> entry : map.entrySet()) {
// 使用預(yù)先獲取的size
}
// 3. 使用局部變量
for (Map.Entry<String, Integer> entry : map.entrySet()) {
final String key = entry.getKey(); // 使用final提高可讀性
final Integer value = entry.getValue();
// 處理邏輯
}
// 4. 考慮使用并行流的閾值
if (map.size() > 10000) {
// 大數(shù)據(jù)量使用并行流
map.entrySet().parallelStream().forEach(entry -> {
process(entry);
});
} else {
// 小數(shù)據(jù)量使用普通遍歷
for (Map.Entry<String, Integer> entry : map.entrySet()) {
process(entry);
}
}六、實(shí)際應(yīng)用案例
6.1 案例一:統(tǒng)計(jì)單詞頻率
public class WordFrequencyCounter {
public static void main(String[] args) {
String text = "java is fun and java is powerful and java is popular";
// 分割單詞并統(tǒng)計(jì)頻率
Map<String, Integer> frequencyMap = new HashMap<>();
String[] words = text.split("\\s+");
for (String word : words) {
frequencyMap.put(word, frequencyMap.getOrDefault(word, 0) + 1);
}
System.out.println("=== 單詞頻率統(tǒng)計(jì) ===");
// 按頻率排序并輸出
frequencyMap.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.forEach(entry -> {
System.out.printf("%-10s: %d次\n", entry.getKey(), entry.getValue());
});
// 找出最常出現(xiàn)的單詞
Map.Entry<String, Integer> mostFrequent = frequencyMap.entrySet().stream()
.max(Map.Entry.comparingByValue())
.orElse(null);
if (mostFrequent != null) {
System.out.println("\n最常出現(xiàn)的單詞: " +
mostFrequent.getKey() + " (" + mostFrequent.getValue() + "次)");
}
}
}6.2 案例二:緩存系統(tǒng)實(shí)現(xiàn)
import java.util.*;
public class SimpleLRUCache<K, V> {
private final int capacity;
private final LinkedHashMap<K, V> cache;
public SimpleLRUCache(int capacity) {
this.capacity = capacity;
this.cache = new LinkedHashMap<K, V>(capacity, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
};
}
public V get(K key) {
return cache.get(key);
}
public void put(K key, V value) {
cache.put(key, value);
}
public void displayCache() {
System.out.println("當(dāng)前緩存內(nèi)容(按訪問順序):");
// 使用entrySet遍歷,保持訪問順序
int index = 1;
for (Map.Entry<K, V> entry : cache.entrySet()) {
System.out.printf("%d. Key: %s, Value: %s\n",
index++, entry.getKey(), entry.getValue());
}
}
public static void main(String[] args) {
SimpleLRUCache<String, String> cache = new SimpleLRUCache<>(3);
cache.put("A", "數(shù)據(jù)A");
cache.put("B", "數(shù)據(jù)B");
cache.put("C", "數(shù)據(jù)C");
cache.displayCache();
// 訪問A,使其成為最近使用的
cache.get("A");
// 添加新數(shù)據(jù),會(huì)淘汰最久未使用的B
cache.put("D", "數(shù)據(jù)D");
System.out.println("\n訪問A后添加D:");
cache.displayCache();
}
}6.3 案例三:配置管理系統(tǒng)
import java.util.*;
public class ConfigurationManager {
private final Map<String, Object> configMap;
public ConfigurationManager() {
configMap = new TreeMap<>(); // 按鍵排序
// 加載默認(rèn)配置
loadDefaultConfig();
}
private void loadDefaultConfig() {
configMap.put("app.name", "MyApplication");
configMap.put("app.version", "1.0.0");
configMap.put("database.url", "localhost:3306");
configMap.put("database.username", "admin");
configMap.put("cache.enabled", true);
configMap.put("cache.size", 1024);
configMap.put("log.level", "INFO");
}
public void displayConfigByCategory() {
System.out.println("=== 應(yīng)用程序配置 ===");
// 按配置類別分組顯示
Map<String, List<String>> categorized = new HashMap<>();
for (Map.Entry<String, Object> entry : configMap.entrySet()) {
String key = entry.getKey();
String category = key.split("\\.")[0]; // 獲取類別
categorized.computeIfAbsent(category, k -> new ArrayList<>())
.add(key + " = " + entry.getValue());
}
// 按類別排序并顯示
categorized.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(categoryEntry -> {
System.out.println("\n[" + categoryEntry.getKey().toUpperCase() + "]");
categoryEntry.getValue().forEach(System.out::println);
});
}
public void updateConfig(String key, Object value) {
if (configMap.containsKey(key)) {
Object oldValue = configMap.get(key);
configMap.put(key, value);
System.out.printf("配置已更新: %s = %s (原值: %s)\n",
key, value, oldValue);
} else {
System.out.println("未知配置項(xiàng): " + key);
}
}
public static void main(String[] args) {
ConfigurationManager config = new ConfigurationManager();
config.displayConfigByCategory();
System.out.println("\n=== 更新配置 ===");
config.updateConfig("log.level", "DEBUG");
config.updateConfig("cache.size", 2048);
System.out.println("\n=== 更新后的配置 ===");
config.displayConfigByCategory();
}
}七、總結(jié)
通過本文的詳細(xì)解析,我們?nèi)媪私饬薐ava中Map集合的五種遍歷方式:
keySet()遍歷:直觀但性能較低,適用于簡(jiǎn)單場(chǎng)景
entrySet()遍歷:性能優(yōu)越,功能全面,最推薦的方式
values()遍歷:只關(guān)心值時(shí)最高效
迭代器遍歷:可在遍歷時(shí)安全刪除元素
Lambda表達(dá)式遍歷:代碼簡(jiǎn)潔,適合Java 8+項(xiàng)目
核心建議:
默認(rèn)選擇entrySet():在大多數(shù)情況下,這是最佳選擇
考慮使用Lambda:如果項(xiàng)目使用Java 8+,可以優(yōu)先考慮forEach
注意線程安全:并發(fā)環(huán)境下使用ConcurrentHashMap或同步機(jī)制
根據(jù)場(chǎng)景選擇:沒有絕對(duì)最好的方式,只有最適合當(dāng)前場(chǎng)景的方式
性能要點(diǎn):
大數(shù)據(jù)量?jī)?yōu)先考慮entrySet()或并行流
頻繁刪除操作必須使用迭代器
只讀遍歷values()最快
希望這篇詳細(xì)的指南能幫助你在實(shí)際開發(fā)中更高效、更優(yōu)雅地遍歷Map集合。根據(jù)具體需求選擇合適的方法,才能寫出既高效又易維護(hù)的代碼。
到此這篇關(guān)于Java Map集合五種遍歷方式詳解與性能對(duì)比的文章就介紹到這了,更多相關(guān)Java Map集合遍歷方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)雙色球抽獎(jiǎng)隨機(jī)算法示例
本篇文章主要介紹了Java實(shí)現(xiàn)雙色球抽獎(jiǎng)隨機(jī)算法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
使用CI/CD工具Github Action發(fā)布jar到Maven中央倉(cāng)庫(kù)的詳細(xì)介紹
今天通過對(duì)Github Action的簡(jiǎn)單使用來介紹了CI/CD的作用,這個(gè)技術(shù)體系是項(xiàng)目集成交付的趨勢(shì),也是面試中的一個(gè)亮點(diǎn)技能。 而且這種方式可以實(shí)現(xiàn)“一次配置,隨時(shí)隨地集成部署”,感興趣的朋友一起看看吧2021-07-07
實(shí)例講解Java并發(fā)編程之ThreadLocal類
這篇文章主要介紹了實(shí)例講解Java并發(fā)編程之ThreadLocal類,本文給出了模擬ThreadLocal、實(shí)用ThreadLocal等代碼實(shí)例,需要的朋友可以參考下2015-04-04
Spring @Value如何通過${}、#{}注入不同類型的值
這篇文章主要介紹了Spring @Value如何通過${}、#{}注入不同類型的值問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
maven在settings.xml和pom.xml中指定jdk版本編譯的方法
在開發(fā)Java應(yīng)用時(shí),通常需要指定要使用的Java版本,下面這篇文章主要給大家介紹了關(guān)于maven在settings.xm和pom.xml中指定jdk版本編譯的方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05
Java 中 Form表單數(shù)據(jù)的兩種提交方式
本文給大家分享java中form表單數(shù)據(jù)的兩種提交方式,分別是get從制定的服務(wù)器中獲取數(shù)據(jù),pos方式提交數(shù)據(jù)給指定的服務(wù)器處理,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧2016-12-12
Spring boot注解@Async線程池實(shí)例詳解
這篇文章主要介紹了Spring boot注解@Async線程池實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12

