1. 介紹
在真實世界中,我們經常需要把每個唯一的「鍵」對應到某個「值」。例如電話簿把人的名字對應到電話號碼,字典把單字對應到它的翻譯,成績表中每位學生都有自己的姓名與相應的分數。
在 Java 中,這類需求可以用介面 Map 來完成。它是用來儲存「鍵—值」配對(key-value pair)的集合。
Map 的主要特性:
- 每個鍵都是唯一的(不允許重複)。
- 一個鍵只能對應一個值。
- 值可以重複。
打個比方:如果清單(List)就像餐廳的排隊(每個人站在自己的位置,可用索引取用),那映射(Map)就像有許多格子的櫃子:每個格子有一個號碼(鍵),裡面放著自己的東西(值)。
Map 介面:基本操作
介面 Map 宣告了處理鍵值配對的基本方法:
| 方法 | 說明 |
|---|---|
|
依鍵新增/取代值 |
|
依鍵取得值 |
|
依鍵刪除鍵值對 |
|
檢查是否存在該鍵 |
|
檢查是否存在該值 |
|
映射中的鍵值對數量 |
|
檢查 Map 是否為空 |
|
刪除所有鍵值對 |
型別 K 與 V 是泛型參數:K(Key)是鍵的型別,V(Value)是值的型別。
3. 類別 HashMap:依鍵快速存取
什麼是 HashMap?
HashMap 是 Map 介面最常見的實作。它能夠依鍵快速存取對應的值。
重要:HashMap 不保證元素的儲存順序!即使你以特定順序加入鍵,遍歷時也可能以不同順序出現。
如何建立 HashMap?
import java.util.HashMap;
import java.util.Map;
public class Example {
public static void main(String[] args) {
// 建立 Map:鍵為 String,值為 Integer
Map<String, Integer> ages = new HashMap<>();
// 新增元素
ages.put("Vasya", 25);
ages.put("Petya", 30);
ages.put("Masha", 22);
// 依鍵取得值
int vasyaAge = ages.get("Vasya");
System.out.println("Vasya 的年齡:" + vasyaAge); // 25
// 檢查是否存在該鍵
if (ages.containsKey("Masha")) {
System.out.println("Masha 在清單中!");
}
// 刪除元素
ages.remove("Petya");
// 遍歷所有鍵值對
for (String name : ages.keySet()) {
System.out.println(name + ": " + ages.get(name));
}
}
}
輸出:
Vasya 的年齡:25
Masha 在清單中!
Vasya: 25
Masha: 22
HashMap 的特點
最需要記住的一點:HashMap 中的鍵必須唯一。如果你放入一個已存在的鍵,新值會覆蓋舊值。
值可以重複:多個不同的鍵可以對應到同一個值。
還有一點很重要——元素的順序。HashMap 不在乎加入順序。輸出時項目的排列可能是打亂的——這是正常行為。
4. 類別 TreeMap:依鍵排序
與 HashMap 不同,TreeMap 會依鍵的排序順序儲存元素。
何時使用 TreeMap?
當你需要元素依鍵遞增(或遞減)順序排列時。例如,你想按照字母順序輸出電話簿。
範例:
import java.util.Map;
import java.util.TreeMap;
public class TreeMapExample {
public static void main(String[] args) {
Map<String, String> phoneBook = new TreeMap<>();
phoneBook.put("Vasya", "+1-900-123-45-67");
phoneBook.put("Masha", "+1-900-555-55-55");
phoneBook.put("Petya", "+1-900-222-33-44");
for (String name : phoneBook.keySet()) {
System.out.println(name + ": " + phoneBook.get(name));
}
}
}
輸出:
Masha: +1-900-555-55-55
Petya: +1-900-222-33-44
Vasya: +1-900-123-45-67
請注意:鍵是依字母順序排序的。
5. Map 的基本操作
新增與覆蓋元素
Map<String, Integer> scores = new HashMap<>();
scores.put("Anna", 90);
scores.put("Ivan", 85);
scores.put("Anna", 95); // 會覆蓋 'Anna' 的值
取得值
Integer annaScore = scores.get("Anna"); // 95
Integer unknown = scores.get("Vasya"); // null,如果沒有該鍵
檢查是否存在鍵或值
scores.containsKey("Ivan"); // true
scores.containsValue(85); // true
依鍵刪除鍵值對
scores.remove("Ivan");
Map 的大小與清空
int size = scores.size();
scores.clear(); // 刪除所有元素
5. 迭代 Map 的元素
Map 不是清單,沒有索引。不過可以這樣遍歷:
按鍵:
for (String key : scores.keySet()) {
System.out.println("鍵:" + key + ",值:" + scores.get(key));
}
按值:
for (Integer value : scores.values()) {
System.out.println("值:" + value);
}
按鍵-值配對(最佳方式):
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + " => " + value);
}
何時用 HashMap,何時用 TreeMap?
HashMap 幾乎是「預設」選擇。如果不在乎鍵的順序、而主要追求操作速度,幾乎總是用它。
當你需要有序時就用 TreeMap。它會自動以排序的鍵儲存資料,並能快速找到最小/最大鍵或處理區間。
總結:在 90% 的情況下選擇 HashMap。當資料一開始就需要「有序」時,使用 TreeMap。
6. Map 的使用範例
範例 1:電話簿
Map<String, String> phoneBook = new HashMap<>();
phoneBook.put("Katya", "+1-999-111-22-33");
phoneBook.put("Oleg", "+1-999-222-33-44");
phoneBook.put("Katya", "+1-999-555-66-77"); // Katya 的舊號碼會被新號碼覆蓋
for (Map.Entry<String, String> entry : phoneBook.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
輸出:
Oleg: +1-999-222-33-44
Katya: +1-999-555-66-77
範例 2:統計單字出現次數
假設我們有一個單字清單,想知道每個單字出現了多少次:
import java.util.*;
public class WordCount {
public static void main(String[] args) {
List<String> words = Arrays.asList("蘋果", "香蕉", "蘋果", "梨子", "香蕉", "蘋果");
Map<String, Integer> counts = new HashMap<>();
for (String word : words) {
int oldCount = counts.getOrDefault(word, 0); // 若沒有該鍵則為 0
counts.put(word, oldCount + 1);
}
System.out.println(counts); // {梨子=1, 蘋果=3, 香蕉=2}
}
}
7. 使用 Map 時的常見錯誤
錯誤 №1:混淆鍵與值。 初學者常會嘗試像在清單一樣用索引取值,或忘了鍵必須唯一。在 Map 中沒有索引——只有鍵。
錯誤 №2:使用 null 鍵與值。 在 HashMap 中允許鍵為 null,但在 TreeMap 中不行(會丟出 NullPointerException)。兩者的值都可以是 null,但實務上很少有幫助。
錯誤 №3:期望 HashMap 中的元素有序。 HashMap 並不保證任何順序。若需要順序——請使用 LinkedHashMap(維持加入順序)或 TreeMap(依鍵排序)。
錯誤 №4:遍歷時同時修改 Map。 如果你在迴圈中遍歷 Map 的同時新增/刪除元素,可能會發生 ConcurrentModificationException。這種情況請使用帶有 remove() 的迭代器,或使用專用的並發集合。
錯誤 №5:用 == 比較鍵與值,而不是 equals。 Map 會用 equals 方法比較鍵(和值)。如果你自己建立作為鍵的類別,務必覆寫 equals 與 hashCode。
GO TO FULL VERSION