Map インターフェースの概要
Map インターフェースは、キーと値のペアを管理するための双列コレクションです。Java では、Map インターフェースを実装するクラスとして以下が主に使用されます:
- HashMap (非スレッドセーフ)
- HashTable (スレッドセーフ)
- TreeMap (非スレッドセーフ)
- LinkedHashMap (スレッドセーフ)
- ConcurrentHashMap (非スレッドセーフ、Concurrent パッケージ)
HashMap と LinkedHashMap は null キーを許容しますが、TreeMap は null キーを許容しません。HashTable は null キーと null 値を両方とも許容しません。
基本操作 (CRUD)
初期化
HashMap map = new HashMap<>();
HashMap map = new HashMap<>(20, 0.8f); // 初期容量 20、ロードファクター 0.8
追加 (Create)
map.put("山田", 25); // 新しいキーを追加
Integer result = map.put("李二郎", 30); // result は null
result = map.put("山田", 26); // result は 25、値が更新される
削除 (Delete)
Integer deletedValue = map.remove("李二郎"); // 削除された値が返る
更新 (Update)
Map インターフェースは直接の更新メソッドを持ちませんが、put() メソッドを用いて値を上書きすることができます。
取得 (Retrieve)
Integer value = map.get("山田"); // 指定したキーの値を取得
常用 API
int size()- Map に含まれる要素数を返すboolean isEmpty()- Map が空であるかを判定するboolean containsKey(Object key)- 指定したキーが存在するかを判定するvoid putAll(Map extends K, ? extends V> m)- 別の Map から要素を追加するvoid clear()- Map を空にするboolean containsValue(Object value)- 指定した値が存在するかを判定するSet- 全てのキーを Set として返すkeySet() Collection- 全ての値を Collection として返すvalues() Set- 全てのキー値ペアを Entry オブジェクトの Set として返す> entrySet()
Map 遍历方法
EntrySet 迭代器
Set> entries = map.entrySet();
Iterator> iterator = entries.iterator();
while(iterator.hasNext()){
Map.Entry entry = iterator.next();
System.out.println("キー: " + entry.getKey() + " === 値: " + entry.getValue());
}
Lambda 表記
map.forEach((key, value) -> {
System.out.println("キー: " + key + " === 値: " + value);
});
KeySet 増強 for ループ
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println("キー: " + key + " === 値: " + map.get(key));
}
Stream API
シングルスレッド
map.entrySet().stream().forEach(entry -> {
System.out.println("キー: " + entry.getKey() + " === 値: " + entry.getValue());
});
マルチスレッド
map.entrySet().parallelStream().forEach(entry -> {
System.out.println("キー: " + entry.getKey() + " === 値: " + entry.getValue());
});
高度なトピック (面接、内部実装)
スレッドセーフな Map
- HashTable - 古い実装、ロック粒度が大きい、非推奨
- Collections.synchronizedMap - 任意の Map をスレッドセーフに変換、性能が劣る、非推奨
- ConcurrentHashMap - 分割ロックを用いてロック粒度を小さくし、性能を向上
拡張メカニズム
HashMap は内部配列の容量がいっぱいになると拡張します。拡張は現在の容量の 2 倍にし、全ての要素を再ハッシュします。
- 初期容量 - 16 がデフォルト
- ロードファクター - 0.75 がデフォルト
- 再ハッシュ (Rehashing) - 拡張時に全ての要素のハッシュ値を再計算
JDK 版本の違い
- JDK 1.7: 配列拡張時に頭から挿入、スレッドUnsafe
- JDK 1.8: 配列拡張時に尾から挿入、チェーンの長さが8以上になると赤黒木に変換