Java HashMap
HashMap 是最常用的 Map 實作,儲存鍵值對,基於雜湊表,存取效率高。
建立 HashMap
import java.util.HashMap;
import java.util.Map;
// 空的 HashMap
Map<String, Integer> map1 = new HashMap<>();
// 指定初始容量
Map<String, Integer> map2 = new HashMap<>(100);
// Java 9+ 建立不可變 Map
Map<String, Integer> map3 = Map.of("A", 1, "B", 2);
// 從不可變 Map 建立可變 Map
Map<String, Integer> map4 = new HashMap<>(Map.of("A", 1, "B", 2));
常用方法
新增/修改
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 100); // 新增
map.put("Banana", 50);
map.put("Apple", 120); // 修改(相同 key)
// 只在 key 不存在時新增
map.putIfAbsent("Cherry", 80);
// 新增多個
map.putAll(Map.of("D", 10, "E", 20));
取得值
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 100);
Integer price = map.get("Apple"); // 100
Integer none = map.get("Unknown"); // null
// 取得值或預設值
Integer value = map.getOrDefault("Unknown", 0); // 0
刪除
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 100);
map.put("Banana", 50);
map.remove("Apple"); // 刪除 key
map.remove("Banana", 50); // 只有 value 也符合才刪除
map.clear(); // 清空
檢查
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 100);
boolean hasKey = map.containsKey("Apple"); // true
boolean hasValue = map.containsValue(100); // true
boolean empty = map.isEmpty(); // false
int size = map.size(); // 1
遍歷
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 100);
map.put("Banana", 50);
map.put("Cherry", 80);
// 遍歷 key
for (String key : map.keySet()) {
System.out.println(key);
}
// 遍歷 value
for (Integer value : map.values()) {
System.out.println(value);
}
// 遍歷 entry(推薦)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// forEach + Lambda
map.forEach((key, value) -> {
System.out.println(key + ": " + value);
});
進階操作
compute
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 100);
// 根據 key 計算新值
map.compute("Apple", (key, value) -> value + 10); // 110
// 只在 key 存在時計算
map.computeIfPresent("Apple", (k, v) -> v * 2);
// 只在 key 不存在時計算
map.computeIfAbsent("Banana", k -> 50);
merge
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 100);
// 合併值
map.merge("Apple", 50, Integer::sum); // 150
map.merge("Banana", 50, Integer::sum); // 50(新 key)
replaceAll
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
// 所有值乘以 10
map.replaceAll((key, value) -> value * 10);
// {A=10, B=20}
實際應用
計數器
String[] words = {"apple", "banana", "apple", "cherry", "banana", "apple"};
Map<String, Integer> counter = new HashMap<>();
for (String word : words) {
counter.merge(word, 1, Integer::sum);
}
// {apple=3, banana=2, cherry=1}
分組
List<String> names = Arrays.asList("Alice", "Bob", "Anna", "Charlie", "Amy");
Map<Character, List<String>> groups = new HashMap<>();
for (String name : names) {
char first = name.charAt(0);
groups.computeIfAbsent(first, k -> new ArrayList<>()).add(name);
}
// {A=[Alice, Anna, Amy], B=[Bob], C=[Charlie]}
快取
Map<String, String> cache = new HashMap<>();
public String getData(String key) {
return cache.computeIfAbsent(key, k -> loadFromDatabase(k));
}
HashMap vs TreeMap vs LinkedHashMap
| 特性 | HashMap | TreeMap | LinkedHashMap |
|---|---|---|---|
| 順序 | 無 | 鍵排序 | 插入順序 |
| 存取 | O(1) | O(log n) | O(1) |
| null key | 允許 | 不允許 | 允許 |
// 需要排序用 TreeMap
Map<String, Integer> treeMap = new TreeMap<>();
// 需要保持插入順序用 LinkedHashMap
Map<String, Integer> linkedMap = new LinkedHashMap<>();
效能特性
| 操作 | 平均時間 | 最差時間 |
|---|---|---|
| get | O(1) | O(n) |
| put | O(1) | O(n) |
| remove | O(1) | O(n) |
| containsKey | O(1) | O(n) |
注意事項
hashCode 和 equals
作為 key 的物件必須正確實作 hashCode() 和 equals():
public class Person {
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
執行緒安全
HashMap 不是執行緒安全的,多執行緒請使用:
// 方式一
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
// 方式二(推薦)
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();