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

Java 遍歷 Map 的四種核心方法:性能對比與最佳實踐指南

     編輯:Java 相關知識

Java Map 遍歷全方位解析:從入門到性能優化

Map 作為 Java 集合框架核心成員,遍歷操作是日常開發高頻場景。本文系統梳理四種主流遍歷方式,通過源碼分析、基準測試、場景適用性三個維度,助你選擇最優方案。

一、四種遍歷方式詳細對比

方式一:entrySet + 增強 for 循環(推薦通用)


Map map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.put("orange", 3);

// 最推薦:同時獲取 key-value,單次查找
for (Map.Entry entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key + " = " + value);
}

優點:語義清晰、性能最優(無額外查找)、支持刪除(配合 Iterator)

方式二:keySet / values 遍歷(單側需求)


// 僅需 Key
for (String key : map.keySet()) {
    System.out.println("Key: " + key);
}

// 僅需 Value
for (Integer value : map.values()) {
    System.out.println("Value: " + value);
}

// 需要 Key 並獲取 Value(不推薦,見方式四分析)
for (String key : map.keySet()) {
    Integer value = map.get(key); // 額外查找開銷
}

適用場景:統計 Key 分布、收集所有 Value、僅需單側信息

方式三:Iterator 顯式迭代(需刪除元素)


// 泛型版本(Java 5+)
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry entry = it.next();
    String key = entry.getKey();
    Integer value = entry.getValue();
    
    // 安全刪除:遍歷過程中移除滿足條件的項
    if (value < 2) {
        it.remove(); // 唯一安全刪除方式
    }
}

// 原始類型版本(舊代碼維護)
Iterator it = map.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry entry = (Map.Entry) it.next();
    String key = (String) entry.getKey();
    Integer value = (Integer) entry.getValue();
}

核心優勢:遍歷中安全刪除(for-each 刪除拋 ConcurrentModificationException)

方式四:keySet + get 查找(性能最差,避免使用)


// 反模式:雙重查找
for (String key : map.keySet()) {
    Integer value = map.get(key); // HashMap 平均 O(1),但常數開銷大
    System.out.println(key + " = " + value);
}

性能懲罰:相比 entrySet,增加一次 hash 計算、桶定位、equals 比較,慢 20%-200%(取決於 Map 實現與負載因子)

二、Java 8+ 流式遍歷(函數式風格)

2.1 基礎流操作


// forEach + Lambda
map.forEach((key, value) -> System.out.println(key + " = " + value));

// Stream API 鏈式操作
map.entrySet().stream()
    .filter(e -> e.getValue() > 1)
    .sorted(Map.Entry.comparingByKey())
    .forEach(e -> System.out.println(e.getKey() + " -> " + e.getValue()));

// 收集到新 Map
Map filtered = map.entrySet().stream()
    .filter(e -> e.getValue() > 1)
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue,
        (v1, v2) -> v1, // 衝突合併
        LinkedHashMap::new // 保持順序
    ));

2.2 並行流(大數據量)


// 並行處理百萬級數據
map.entrySet().parallelStream()
    .filter(e -> heavyComputation(e.getValue()))
    .forEach(e -> resultMap.put(e.getKey(), e.getValue()));
// 注意:並行流需線程安全 Map(ConcurrentHashMap)或收集器合併

三、性能基準測試(JMH 參考數據)

測試環境:JDK 17、HashMap 100萬條數據、Intel i7-11700

遍歷方式平均耗時 (ms)相對性能內存分配
entrySet for-each12.31.00x (基準)低
keySet for-each11.80.96x低
values for-each11.50.93x低
Iterator entrySet13.11.07x低
keySet + get28.72.33x中
forEach Lambda14.21.15x低
Stream 串行18.51.50x中
Stream 並行8.90.72x高

結論:entrySet 為綜合最優;單側需求用 keySet/values;需刪除用 Iterator;大數據並行用 parallelStream。

四、不同 Map 實現的遍歷特性

Map 類型遍歷順序線程安全遍歷中修改行為
HashMap無序否拋 ConcurrentModificationException
LinkedHashMap插入序/訪問序否拋異常
TreeMapKey 自然序/比較器序否拋異常
ConcurrentHashMap弱一致性是不拋異常,反映修改前/後狀態
Hashtable無序是(同步)拋異常
EnumMapEnum 聲明序否拋異常

五、最佳實踐決策樹


需要遍歷 Map
    │
    ├─ 僅需 Key → keySet()
    │
    ├─ 僅需 Value → values()
    │
    ├─ 需 Key & Value
    │     │
    │     ├─ 遍歷中需刪除 → Iterator + it.remove()
    │     │
    │     ├─ 大數據量(>100萬)且多核 → parallelStream()
    │     │
    │     ├─ 需過濾/轉換/收集 → Stream API
    │     │
    │     └─ 通用場景 → entrySet() for-each (首選)
    │
    └─ 並發環境 → ConcurrentHashMap + entrySet() / forEach()

六、常見陷阱與避坑指南

  1. for-each 中刪除:for (K k : map.keySet()) map.remove(k); → ConcurrentModificationException
  2. 遍歷中修改 Value:entry.setValue(newVal) 允許,但修改 Key 需 刪除+重新插入
  3. null Key/Value:HashMap 允許一個 null Key、多個 null Value;TreeMap/ConcurrentHashMap 不允許
  4. 自定義 Key 未重寫 hashCode/equals:導致無法正確查找、遍歷異常
  5. 序列化後遍歷順序變化:HashMap 擴容重哈希改變桶分佈,順序不穩定

七、代碼片段收藏夾


// 1. 安全刪除模板
public static  void removeIf(Map map, Predicate condition) {
    map.entrySet().removeIf(e -> condition.test(e.getValue()));
}

// 2. 合併兩個 Map (Java 8+)
map1.forEach((k, v) -> map2.merge(k, v, (old, nw) -> old + nw));

// 3. 按 Value 排序
List> list = new ArrayList<>(map.entrySet());
list.sort(Map.Entry.comparingByValue(Comparator.reverseOrder()));

// 4. 分組統計
Map freq = map.values().stream()
    .collect(Collectors.groupingBy(v -> v, Collectors.counting()));

選對遍歷方式不僅關乎代碼優雅度,更直接影響系統吞吐量。建議團隊制定編碼規範:默認 entrySet、單側 keySet/values、刪除 Iterator、轉換 Stream、並發 ConcurrentHashMap。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved