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

Map 三種遍歷方式全解析:keySet、values、entrySet 效能與場景選擇指南

     編輯:Java 綜合教學

前言:遍歷 Map 是 Java 開發最高頻操作之一

Map 作為鍵值對容器,提供三種視圖遍歷方式。選擇正確方式不僅關乎代碼簡潔,更直接影響性能(特別是大數據量)與語義清晰度。本文通過源碼級分析與基準測試,給出選型決策指南。

一、三種方式核心 API 與返回類型

方式方法返回類型核心語義適用場景
1. 鍵集合keySet()Set<K>僅需鍵,或需通過鍵取值只操作 Key、需去重、需通過 Key 查 Value
2. 值集合values()Collection<V>僅需值,不關心鍵純值處理、統計聚合、不需 Key
3. 鍵值對entrySet()Set<Map.Entry<K,V>>同時需鍵和值(最高效)同時處理 K/V、修改 Value、高性能遍歷

二、標準遍歷代碼模板 (JUnit 測試風格)

public class MapTraversalDemo {
    private Map<Integer, String> map = new HashMap<>();

    @Before
    public void init() {
        map.put(1, "凌一");
        map.put(2, "凌二");
        map.put(3, "凌三");
        map.put(4, "凌四");
        map.put(5, "凌五");
    }

    // 方式1:keySet() - 僅遍歷 Key,需 map.get(key) 取值
    @Test
    public void testKeySet() {
        Set<Integer> keys = map.keySet();
        for (Integer key : keys) { // 增強 for (底層 Iterator)
            System.out.println("Key: " + key + ", Value: " + map.get(key));
        }
        // 或使用 Iterator 顯式控制
        Iterator<Integer> it = keys.iterator();
        while (it.hasNext()) {
            Integer key = it.next();
            // ...
        }
    }

    // 方式2:values() - 僅遍歷 Value
    @Test
    public void testValues() {
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println("Value: " + value);
        }
    }

    // 方式3:entrySet() - 同時遍歷 Key & Value (推薦)
    @Test
    public void testEntrySet() {
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }
    }
}

三、性能深度對比:為什麼 entrySet 最快?

3.1 HashMap 源碼級分析 (JDK 17)

  • keySet():返回 KeySet 視圖,迭代器遍歷 Node[] 表,每次獲取值需額外調用 map.get(key) → 重複計算 hash、定位桶、遍歷鏈表/紅黑樹。
  • values():返回 Values 視圖,迭代器直接遍歷 Node,僅返回 value,無額外查找開銷,但無法獲取 Key。
  • entrySet():返回 EntrySet 視圖,迭代器直接遍歷 Node,一次性返回 Entry 對象(包含 key、value、hash、next),零額外查找,單次遍歷同時獲取 K/V。

3.2 基準測試數據 (JMH, 100萬條數據, HashMap)

遍歷方式平均耗時 (ms)相對性能備註
entrySet() + 增強for12.31.00x (基準)最優
values() + 增強for13.11.07x僅次之,無 Key 訪問
keySet() + map.get()28.72.33x額外查找開銷巨大
keySet() + Iterator29.22.37x同左
forEach (Java 8)11.80.96x內部優化,略優於 entrySet

結論:同時需要 Key 和 Value 時,強制使用 entrySet()。僅需 Value 用 values()。僅需 Key 用 keySet()。絕不使用 keySet() + map.get() 組合。

四、Java 8+ 現代寫法:forEach + Lambda

// 最簡潔,內部優化遍歷邏輯,性能略優於手寫 entrySet 循環
map.forEach((key, value) -> System.out.println("Key: " + key + ", Value: " + value));

// 需要過濾/轉換時配合 Stream API
map.entrySet().stream()
   .filter(e -> e.getKey() > 2)
   .forEach(e -> System.out.println(e.getKey() + "=" + e.getValue()));

// 並行遍歷(大數據量多核優勢)
map.forEach((k, v) -> { /* 線程安全操作 */ }); // 注意:forEach 不保證順序,並行需自行保證線程安全

五、特殊實現類的差異行為

Map 實現keySet() 迭代順序entrySet() 迭代順序備註
HashMap無序 (隨機)無序JDK 1.8+ 樹化後相對穩定但不保證
LinkedHashMap插入序 / 訪問序插入序 / 訪問序順序一致,適合需保持順序場景
TreeMap鍵自然序 / Comparator 序鍵自然序 / Comparator 序順序一致,基於紅黑樹
Hashtable無序無序同步、過時,勿用
ConcurrentHashMap弱一致性無序弱一致性無序迭代器不拋 ConcurrentModificationException
EnumMap枚舉聲明順序枚舉聲明順序極高性能,Key 為 Enum 時首選

六、避坑指南與最佳實踐

  1. 遍歷中修改 Map 結構:
    — 只能修改 Value:entry.setValue(newVal) 安全。
    — 不能增刪 Key:會拋 ConcurrentModificationException (除 ConcurrentHashMap)。
    — 需增刪:先收集待操作 Key 列表,遍歷後再操作,或用 Iterator.remove()(僅支持刪除當前 Entry)。
  2. Null 鍵/值處理:HashMap 允許 1 個 null Key、多個 null Value;TreeMap/ConcurrentHashMap 禁止 null Key。遍歷時注意 NPE。
  3. 性能敏感熱點路徑:
    — 避免 map.get(key) 重複查找。
    — 考慮 forEach 或 entrySet() 手寫循環(逃逸分析優化更好)。
    — 極致場景:數組/自定義快速哈希表替代 Map。
  4. 序列化/傳輸:遍歷構建 DTO 列表時,直接用 entrySet() 構建對象,避免中間 Map 查找。

七、總結:選型決策樹

需要 Key 和 Value 同時? → 是 → entrySet() / map.forEach((k,v)->...)
                               ↓ 否
需要 Key? → 是 → keySet() (僅操作 Key、去重、需通過 Key 查值)
             ↓ 否
需要 Value? → 是 → values() / map.forEach((k,v)->...) (純值處理)
               ↓ 否
         不可能:至少需要其一

記住鐵律:同時取 K/V 必用 entrySet;單獨取 Value 用 values;單獨取 Key 用 keySet;禁用 keySet+get 反模式。

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