四種常見遍歷寫法
① keySet + get(最慢、最常見的反模式)
for (K key : map.keySet()) {
V value = map.get(key); // 額外 hash 查找
}
- 每次
get 觸發一次 hash 計算與桶定位
- FindBugs 會標記為「低效迭代」
- 所有 Map 實現中比 entrySet 慢 20%~200%
② Iterator 遍歷 entrySet(可刪除、效能優)
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry e = it.next();
K k = e.getKey(); V v = e.getValue();
it.remove(); // 安全刪除
}
③ for-each 遍歷 keySet/values(不可刪除、中等)
for (K key : map.keySet()) { ... }
for (V value : map.values()) { ... }
④ for-each 遍歷 entrySet(效能最佳、不可刪除)
for (Map.Entry e : map.entrySet()) {
K k = e.getKey(); V v = e.getValue();
}
實測數據(10 萬筆資料,單位 ms)
| 輪次 | keySet+get | Iterator entrySet | for-each keySet+values | for-each entrySet |
| 1 | 37 | 32 | 39 | 13 |
| 2 | 29 | 18 | 32 | 15 |
| 3 | 50 | 57 | 39 | 21 |
| 4 | 47 | 31 | 39 | 14 |
結論與最佳實踐
- 唯讀遍歷:
for (Entry e : map.entrySet()) —— 最快、語意清晰
- 需邊遍歷邊刪除:
Iterator it = map.entrySet().iterator() —— 唯一安全且高效方式
- 拒絕 keySet + get—— 除非你只需要 key,完全不碰 value
- StackOverflow 傳言「keySet 快 10%」在 JDK 8+ 已失效,實測 entrySet 穩定領先 2~3 倍
原理補充
entrySet 直接暴露 Node[] 陣列中的節點,零額外查找
keySet/values 迭代器內部仍需透過 nextNode() 取節點再取 key/value,多一層包裝
- JDK 8 引入
forEach(BiConsumer) 內部同樣走 entrySet 邏輯,效能等同寫法 ④