1. 前言
我們已經知道,ArrayList 是一個會自己成長的「無限陣列」。但它有個限制:只能透過編號(索引)來存取元素。如果有一份包含一千人的清單,而我們想找到 Alice 的電話,就必須不是已知她的索引,就是得把所有人逐一遍歷。
在現實中,我們常常依據唯一的特徵來查找資料:
- 依姓名找電話號碼,
- 依護照號碼找持有人,
- 依登入名找到密碼,
- 依 id 在資料庫中尋找物件。
因此我們需要一種結構,能夠「拿到鍵 → 立刻找到值」。在 Java 中這就是 HashMap<K,V> 的職責。
類比:
請想像一本紙本字典。你要查單字「house」的翻譯,不需要一頁頁翻。你會直接翻到字母「H」,然後找到對應的單字。HashMap 也是如此:依鍵(單字)就能瞬間取得值(翻譯)。
建立字典
要建立字典,必須指定哪些型別作為鍵與值。
import java.util.HashMap;
HashMap<String, String> phonebook = new HashMap<String, String>();
說明:
- String(第一個型別)是鍵的型別(人的姓名)。
- String(第二個型別)是值的型別(電話號碼)。
現在 phonebook 就像一本真正的電話簿。
其他常見的變體:
HashMap<String, Integer> grades = new HashMap<String, Integer>(); // 姓名 → 成績
HashMap<Integer, String> users = new HashMap<Integer, String>(); // id → 姓名
HashMap<String, Boolean> flags = new HashMap<String, Boolean>(); // 鍵 → 布林值
與 ArrayList 不同,字典沒有 add() 方法,但有一組同樣有趣的方法。
2. 方法 put(key, value) — 新增一組鍵值
HashMap 與陣列、串列的主要差異在於,它不只是儲存一堆值,而是儲存鍵–值配對。這使它像真正的字典:每個單字有翻譯、每個姓名有電話、每個登入名有密碼。
範例:
phonebook.put("Alice", "+380501112233");
phonebook.put("Bob", "+380671234567");
現在在我們的電話簿中:
- 鍵 "Alice" 對應值 "+10501112233",
- 鍵 "Bob" 對應值 "+10671234567"。
這表示要取得值(電話),只要知道鍵(姓名)就行。
重要:鍵是唯一的。如果嘗試以已存在的鍵加入新值,舊的值會被覆寫。
phonebook.put("Alice", "+10999999999");
現在 Alice 只剩下新號碼;舊號碼已經被覆蓋了。
結論:put 是用來新增或更新紀錄的方法。鍵是新的—就建立紀錄;鍵已存在—就更新紀錄。
3. 方法 get(key) — 取得值
要找到值,需要知道鍵。這正是 HashMap 的關鍵:依鍵查找非常快,幾乎是瞬間,無論我們有一千筆或一百萬筆資料。
範例:
System.out.println(phonebook.get("Alice"));
輸出:
+10501112233
如果沒有該鍵,get 會回傳 null:
System.out.println(phonebook.get("Charlie")); // null
這表示:「字典中沒有鍵 'Charlie'」。
4. 方法 containsKey(key) — 檢查是否存在
為了避免遇到 null,先檢查字典是否包含該鍵會很有幫助。
範例:
System.out.println(phonebook.containsKey("Charlie"));
輸出:
false
因此我們可以事先判斷:
- 若沒有該鍵——建立新紀錄,
- 若已有該鍵——更新紀錄。
常見寫法:
if (phonebook.containsKey("Alice"))
{
System.out.println("Alice 已經有號碼了!");
}
else
{
phonebook.put("Alice", "+10111111111");
}
5. 方法 remove(key) — 刪除紀錄
刪除同樣很簡單:只需要知道鍵。
phonebook.remove("Bob");
System.out.println(phonebook.get("Bob"));
輸出:
null
現在鍵為 "Bob" 的紀錄已不存在於字典中。
6. 遍歷所有配對
我們常常不只要根據特定鍵取得資料,也需要把整個字典列出。這時可以使用 entrySet() 方法。
for (var entry : phonebook.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
範例輸出:
Alice -> +10501112233
如此一來,我們同時取得鍵(entry.getKey())與值(entry.getValue())的存取權限。
7. 實作:統計單字次數
來看一個經典問題——統計文字中單字出現的次數。這是 HashMap 的最佳應用之一。
String text = "java java core java";
HashMap<String, Integer> freq = new HashMap<String, Integer>();
for (String w : text.split(" "))
{
Integer old = freq.get(w);
freq.put(w, (old == null) ? 1 : old + 1);
}
System.out.println(freq);
輸出:
{core=1, java=3}
詳細說明如下:
- 我們把字串 "java java core java" 依空白切成單字。
- 對於每個單字,檢查它是否已在字典中(freq.get(w))。
- 如果沒有(null),代表是第一次出現 → 放入 1。
- 如果已存在,代表曾經出現過 → 將值加一。
實際用途:
- 計算對 API 的呼叫次數,
- 文本中的單字頻率統計,
- 倉庫商品數量的儲存。
8. 實作:電話簿
我們來寫個更完整的小應用。
import java.util.HashMap;
import java.util.Scanner;
public class PhonebookApp {
public static void main(String[] args)
{
HashMap<String, String> phonebook = new HashMap<String, String>();
Scanner console = new Scanner(System.in);
while (true)
{
System.out.print("請輸入姓名(或輸入空行以退出): ");
String name = console.nextLine();
if (name.isEmpty()) break;
System.out.print("請輸入電話號碼: ");
String phone = console.nextLine();
phonebook.put(name, phone);
}
System.out.println("電話簿:");
for (var entry : phonebook.entrySet())
{
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
}
這個程式就像迷你查詢工具。你輸入姓名與電話,它們會被儲存在 HashMap 中。最後可以把整個清單列出。字典在真實專案中非常常見。
GO TO FULL VERSION