Map 是鍵值映射的核心抽象,取代舊版 Hashtable 並提供豐富的集合視圖操作。本文系統剖析 Map 介面設計、基本/批量操作、集合視圖威力、Map 代數實戰技巧,並以排列組搜尋示範多重映射模式。
public interface Map<K,V> {
// 基本操作
V put(K key, V value);
V get(Object key);
V remove(Object key);
boolean containsKey(Object key);
boolean containsValue(Object value);
int size(); boolean isEmpty();
// 批量操作
void putAll(Map<? extends K, ? extends V> m);
void clear();
// 集合視圖
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K,V>> entrySet();
// Entry 介面
interface Entry<K,V> {
K getKey(); V getValue(); V setValue(V value);
}
}
JDK 提供 HashMap(雜湊表,最佳效能)與 TreeMap(紅黑樹,有序迭代)兩大通用實現;Hashtable 已改造實現 Map。
| 特性 | Map | Hashtable |
|---|---|---|
| 集合視圖 | 三種視圖支援迭代 | 僅 Enumeration |
| 鍵值對迭代 | entrySet() 直接支援 | 無 |
| 迭代中安全移除 | Iterator.remove() | 不可 |
| contains 方法 | 分離 containsKey/containsValue | 單一 contains 易混淆 |
Map<String, Integer> freq = new HashMap<>();
for (String word : args) {
Integer cnt = freq.get(word);
freq.put(word, (cnt == null) ? 1 : cnt + 1);
}
// 輸出: {to=3, me=1, delegate=1, it=2, is=2, if=1, be=1, up=1}
只需將 HashMap 換成 TreeMap 即得字母順序輸出——介面導向編程的威力。
clear():清空所有映射putAll(m):傾倒另一 Map,**巧用默認值合併**:static Map<String, String> newAttributeMap(Map<String, String> defaults,
Map<String, String> overrides) {
Map<String, String> result = new HashMap<>(defaults); // 標準建構子複製
result.putAll(overrides); // 覆蓋
return result;
}
keySet():鍵集合(Set)values():值集合(Collection,非 Set,值可重複)entrySet():鍵值對集合(Set of Map.Entry)
// 鍵迭代
for (Iterator<K> i = map.keySet().iterator(); i.hasNext(); )
System.out.println(i.next());
// 鍵值對迭代(最高效)
for (Iterator<Map.Entry<K,V>> i = map.entrySet().iterator(); i.hasNext(); ) {
Map.Entry<K,V> e = i.next();
System.out.println(e.getKey() + ": " + e.getValue());
}
remove、removeAll、retainAll、clear、Iterator.removeentrySet 視圖支援 Entry.setValue 修改值(**迭代中修改 Map 的唯一安全途徑**)add/addAll 無意義(鍵值對需完整)同一 Map 多次呼叫 keySet() 返回**同一物件**,無效能顧慮。
批量操作 containsAll/removeAll/retainAll 將 Map 問題轉化為集合運算:
// 子映射檢查
if (m1.entrySet().containsAll(m2.entrySet())) { ... }
// 鍵集合相等檢查
if (m1.keySet().equals(m2.keySet())) { ... }
// 屬性驗證:必要屬性、合法屬性
Set<String> attrs = attrMap.keySet();
if (!attrs.containsAll(required)) { /* 缺漏報錯 */ }
if (!permitted.containsAll(attrs)) { /* 非法報錯 */ }
// 共同鍵
Set<K> commonKeys = new HashSet<>(a.keySet());
commonKeys.retainAll(b.keySet());
// 刪除共同鍵值對
m1.entrySet().removeAll(m2.entrySet());
// 刪除在另一 Map 有映射的鍵
m1.keySet().removeAll(m2.keySet());
// managers: 員工 → 管理者
// 找出個體貢獻者(非管理者)
Set<Employee> contributors = new HashSet<>(managers.keySet());
contributors.removeAll(managers.values());
// 辭退 Herbert 直屬下屬
managers.values().removeAll(Collections.singleton(herbert));
// 找出管理者已離職的員工
Map<Employee, Employee> tmp = new HashMap<>(managers);
tmp.values().removeAll(managers.keySet()); // 移除值仍為鍵的項
Set<Employee> orphaned = tmp.keySet(); // 孤兒員工
Framework 未內建 Multimap,但 Map<K, List<V>> 完美替代。案例:字典排列組搜尋。
"bad" → "abd" (鍵) → "bad" (值)
"dab" → "abd" (鍵) → "dab" (值)
// 同鍵自動形成排列組
Map<String, List<String>> multimap = new HashMap<>();
try (BufferedReader in = new BufferedReader(new FileReader(dictFile))) {
String word;
while ((word = in.readLine()) != null) {
String key = alphabetize(word); // 桶式排序實作
List<String> list = multimap.get(key);
if (list == null) multimap.put(key, list = new ArrayList<>());
list.add(word);
}
}
// 輸出達標排列組
for (List<String> group : multimap.values())
if (group.size() >= minSize)
System.out.println(group.size() + ": " + group);
8 萬單詞字典,16 秒內完成,展示 Map+List 組合處理複雜映射關係的實力。
Collections.sort(list) 自動依據元素實作的 Comparable 排序。JDK 核心類自然順序表:
| 類別 | 自然順序 |
|---|---|
| String | 字典序 |
| Date | 時間順序 |
| Integer/Long/Double… | 數值順序 |
| File | 路徑名字典序(系統相關) |
| CollationKey | 特定語系字典序 |
元素未實作 Comparable 或無法相互比較 → ClassCastException。
Map 介面設計精妙,集合視圖將鍵值映射轉化為集合運算,配合多重映射模式,能優雅解決大量實務問題。