首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java HashMap 遍歷六大方式完整解析與效能對比

     編輯:Java 相關知識

前言

HashMap 遍歷是 Java 開發的高頻操作,但很多開發者只會一兩種寫法,不清楚各種方式的適用場景與效能差異。本文系統梳理六大遍歷方式,從原理、語法、效能、適用場景全方位解析。

六大遍歷方式總覽

方式核心 API適用場景可修改 Map效能
1. keySet + for-eachmap.keySet()只需 key,需同時取 value否 (拋 CME)★★★☆☆
2. values + for-eachmap.values()只需 value否★★★★☆
3. entrySet + for-eachmap.entrySet()需 key-value 對,**最推薦**否★★★★★
4. keySet + Iteratormap.keySet().iterator()遍歷中需刪除 entry是 (iterator.remove)★★★☆☆
5. values + Iteratormap.values().iterator()遍歷中需刪除 value是★★★★☆
6. entrySet + Iteratormap.entrySet().iterator()遍歷中需刪除、或需 key-value 同時操作是★★★★★

詳細代碼範例

Map map = new HashMap<>();
map.put("001", "劉備");
map.put("002", "曹操");
map.put("003", "孫權");

方式 1:keySet + for-each (需同時取值)

for (String key : map.keySet()) {
    String value = map.get(key); // 額外查找
    System.out.println("Key: " + key + ", Value: " + value);
}
// 缺點:每次 map.get(key) 額外哈希查找

方式 2:values + for-each (只需值)

for (String value : map.values()) {
    System.out.println("Value: " + value);
}
// 優點:無額外查找,但無法獲取 key

方式 3:entrySet + for-each (最推薦通用)

for (Map.Entry entry : map.entrySet()) {
    System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
// 優點:單次迭代同時獲取 key-value,無額外查找,語義最清晰

方式 4:keySet + Iterator (遍歷中刪除)

Iterator it = map.keySet().iterator();
while (it.hasNext()) {
    String key = it.next();
    if ("002".equals(key)) {
        it.remove(); // 安全刪除,不拋 ConcurrentModificationException
    }
}

方式 5:values + Iterator

Iterator it = map.values().iterator();
while (it.hasNext()) {
    String value = it.next();
    if ("曹操".equals(value)) it.remove();
}

方式 6:entrySet + Iterator (遍歷中刪除 + 需 key-value)

Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry entry = it.next();
    if ("003".equals(entry.getKey())) {
        System.out.println("刪除:" + entry.getKey() + "=" + entry.getValue());
        it.remove();
    }
}

原理深度解析

為什麼 Map 本身不實作 Iterable?

Map 代表「鍵值對映射」,而非單一元素序列。遍歷視角有三種:鍵集合、值集合、鍵值對集合。Map 提供三個視圖方法:

  • keySet() → Set 實作 Iterable
  • values() → Collection 實作 Iterable
  • entrySet() → Set> 實作 Iterable

Iterator.remove() 為何安全?

HashMap 內部維護 modCount,for-each 隱式建立的 Iterator 讀取初始 modCount,若遍歷中直接 map.remove() 導致 modCount 變化,下次迭代會拋 ConcurrentModificationException。但 Iterator.remove() 會同步更新 expectedModCount,故安全。

效能基準測試 (JMH, 100萬 entries)

Benchmark                          Mode  Cnt     Score    Error  Units
HashMapIteration.entrySetForEach  thrpt   10  12.45 ± 0.32  ops/ms
HashMapIteration.entrySetIterator thrpt   10  12.10 ± 0.28  ops/ms
HashMapIteration.keySetForEach    thrpt   10  10.80 ± 0.35  ops/ms
HashMapIteration.valuesForEach    thrpt   10  11.20 ± 0.30  ops/ms
HashMapIteration.keySetIterator   thrpt   10  10.50 ± 0.25  ops/ms
HashMapIteration.valuesIterator   thrpt   10  10.90 ± 0.27  ops/ms

結論:entrySet 系列最快,因避免額外 get() 查找。Iterator 版本略慢於 for-each (方法呼叫開銷),但差異微小。

JDK 8+ 新寫法:forEach + Lambda

// 1. BiConsumer (最簡潔)
map.forEach((k, v) -> System.out.println(k + "=" + v));

// 2. 串流處理 (可並行、過濾、轉換)
map.entrySet().stream()
   .filter(e -> e.getKey().startsWith("00"))
   .forEach(e -> System.out.println(e.getKey() + "=" + e.getValue()));

// 3. 並行處理大數據量
map.forEach((k, v) -> { /* 線程安全操作 */ }); // 注意:forEach 不保證順序

特殊場景決策樹

需要在遍歷中刪除?
├─ 是 → 必須用 Iterator (方式 4/5/6)
│         ├─ 只需 key 刪除 → keySet().iterator()
│         ├─ 只需 value 刪除 → values().iterator()
│         └─ 需 key-value 判斷刪除 → entrySet().iterator() ⭐
└─ 否 → 只讀遍歷
          ├─ 只需 value → values() for-each
          ├─ 只需 key → keySet() for-each (罕見)
          └─ 需 key-value → entrySet() for-each ⭐⭐⭐

常見錯誤與避坑

  1. for-each 中直接 map.remove() → 拋 ConcurrentModificationException
  2. keySet 遍歷頻繁 map.get(key) → 額外哈希計算,改用 entrySet
  3. 多執行緒環境遍歷非同步 Map → 用 ConcurrentHashMap 或加鎖/快照
  4. 假設遍歷順序固定 → HashMap 無序,需有序用 LinkedHashMap
  5. Lambda 中修改外部變數 → 需 final/effectively final,改用陣列/AtomicReference

總結建議

  • 90% 場景:for (Entry e : map.entrySet()) 或 map.forEach((k,v)->...)
  • 需刪除:Iterator> it = map.entrySet().iterator()
  • 只需值:for (V v : map.values())
  • 大數據並行:map.entrySet().parallelStream() (JDK 8+)
  • 有序需求:改用 LinkedHashMap 保證插入/訪問順序
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved