8.

Java Map (連想配列) 完全ガイド

編集
この記事の要点
  • Java の連想配列は Map<K, V> インタフェース。実装は HashMap / LinkedHashMap / TreeMap / ConcurrentHashMap
  • HashMap: 順序なし、最速。一般用途
  • LinkedHashMap: 挿入順を保持。LRU キャッシュにも
  • TreeMap: キーでソート済み。範囲検索 (subMap) 可
  • ConcurrentHashMap: スレッドセーフ。並列性高い
  • Java 9+ Map.of("a", 1, "b", 2) で不変 Map を 1 行作成
  • 基本操作: put / get / remove / containsKey / containsValue
  • 反復は entrySet() 経由が定番。Stream API も使える

Java Map の基本

Java で連想配列 (キーと値のペア) を扱うには java.util.Map インタフェースを使います。具体的な実装クラスをいくつか選びます:

import java.util.*;

// 1. HashMap (順序なし、最速、一般用途)
Map<String, Integer> map = new HashMap<>();
map.put("apple", 100);
map.put("banana", 200);
map.put("cherry", 300);

System.out.println(map.get("apple"));   // 100
System.out.println(map.size());          // 3

// 2. LinkedHashMap (挿入順を保持)
Map<String, Integer> linked = new LinkedHashMap<>();
linked.put("a", 1);
linked.put("b", 2);
linked.put("c", 3);
// 反復順: a, b, c (常に挿入順)

// 3. TreeMap (キーでソート)
Map<String, Integer> tree = new TreeMap<>();
tree.put("cherry", 3);
tree.put("apple", 1);
tree.put("banana", 2);
// 反復順: apple, banana, cherry (キー昇順)

// 4. Map.of (Java 9+, 不変)
Map<String, Integer> immut = Map.of("a", 1, "b", 2, "c", 3);
// immut.put("d", 4);  // UnsupportedOperationException

// 10 ペア以上は Map.ofEntries
Map<String, Integer> many = Map.ofEntries(
    Map.entry("a", 1),
    Map.entry("b", 2),
    Map.entry("c", 3)
);

主な操作メソッド

メソッド動作戻り値
put(k, v)キーと値を追加/上書き旧値 (なければ null)
get(k)キーで値取得値 (なければ null)
getOrDefault(k, def)取得 or デフォルト値 or def
remove(k)キーで削除削除した値 (なければ null)
containsKey(k)キー存在チェックboolean
containsValue(v)値存在チェック (遅い: O(n))boolean
size()件数int
isEmpty()空チェックboolean
clear()全削除void
keySet()キーの SetSet<K>
values()値の CollectionCollection<V>
entrySet()エントリの SetSet<Map.Entry<K,V>>
putIfAbsent(k, v)無いときだけ追加旧値
compute(k, fn)関数で値計算更新新値
merge(k, v, fn)あればマージ関数新値

反復処理

Map<String, Integer> map = new HashMap<>();
map.put("apple", 100);
map.put("banana", 200);

// 1. entrySet (推奨: キーと値両方)
for (Map.Entry<String, Integer> e : map.entrySet()) {
    System.out.println(e.getKey() + " = " + e.getValue());
}

// 2. forEach (Java 8+, ラムダ)
map.forEach((key, val) -> System.out.println(key + " = " + val));

// 3. keySet (キーだけ)
for (String key : map.keySet()) {
    System.out.println(key);
}

// 4. values (値だけ)
for (Integer val : map.values()) {
    System.out.println(val);
}

// 5. Stream API
map.entrySet().stream()
   .filter(e -> e.getValue() > 100)
   .forEach(e -> System.out.println(e.getKey()));

compute / merge / putIfAbsent パターン

// 単語の出現回数カウント (定番パターン)
Map<String, Integer> count = new HashMap<>();
String[] words = {"apple", "banana", "apple", "cherry", "apple"};

// ❌ 冗長
for (String w : words) {
    if (count.containsKey(w)) {
        count.put(w, count.get(w) + 1);
    } else {
        count.put(w, 1);
    }
}

// ✅ merge (推奨)
for (String w : words) {
    count.merge(w, 1, Integer::sum);
}

// ✅ compute も使える
for (String w : words) {
    count.compute(w, (k, v) -> v == null ? 1 : v + 1);
}

// 配列を値にする (グルーピング)
Map<String, List<String>> grouped = new HashMap<>();
for (String item : List.of("a1", "b1", "a2", "b2", "a3")) {
    String key = item.substring(0, 1);
    grouped.computeIfAbsent(key, k -> new ArrayList<>()).add(item);
}
// {a=[a1,a2,a3], b=[b1,b2]}

// Stream.collect なら 1 行
var byPrefix = Stream.of("a1", "b1", "a2", "b2", "a3")
    .collect(Collectors.groupingBy(s -> s.substring(0, 1)));

実装クラスの選び方

用途推奨実装計算量 (get/put)
一般用途HashMapO(1)
挿入順を保ちたいLinkedHashMapO(1)
LRU キャッシュLinkedHashMap (accessOrder=true)O(1)
キーでソートTreeMapO(log n)
範囲検索TreeMap (subMap/headMap/tailMap)O(log n)
並行処理ConcurrentHashMapO(1) 並列性高い
不変 (定数 Map)Map.of / Map.copyOfO(1)

ConcurrentHashMap (スレッドセーフ)

import java.util.concurrent.ConcurrentHashMap;

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// 通常の Map と同じ API
map.put("a", 1);
map.put("b", 2);

// 並列カウントは atomic に
map.merge("a", 1, Integer::sum);    // スレッドセーフに +1

// 並列 forEach
map.forEachEntry(1, e -> System.out.println(e.getKey() + "=" + e.getValue()));

// compute は atomic
map.compute("counter", (k, v) -> v == null ? 1 : v + 1);

// ⚠️ HashMap を複数スレッドから put すると、まれに無限ループになることがある (Java 7 以前)
// → 並列ならば必ず ConcurrentHashMap

null キー / null 値の扱い

実装null キーnull 値
HashMap1 個まで OKOK
LinkedHashMap1 個まで OKOK
TreeMapNG (NullPointerException)OK
ConcurrentHashMapNGNG
Map.ofNGNG

不変 Map と防御コピー

// 不変 Map (Java 9+)
Map<String, Integer> immut = Map.of("a", 1, "b", 2);
// immut.put("c", 3);  // UnsupportedOperationException

// 既存 Map を不変にコピー (Java 10+)
Map<String, Integer> copy = Map.copyOf(originalMap);

// Collections.unmodifiableMap (ビュー、元が変わると見える)
Map<String, Integer> view = Collections.unmodifiableMap(originalMap);

// Stream で不変 Map 構築
Map<String, Integer> built = Stream.of("apple", "banana")
    .collect(Collectors.toUnmodifiableMap(
        s -> s,
        String::length
    ));

FAQ

Q: map.get("x") が null を返す → キーが無い? 値が null?
A: 区別できないので containsKey() を併用、または getOrDefault でデフォルト値を返す設計に。

Q: HashMap の初期容量は指定したほうがいい?
A: 大量データ (1 万件以上) を入れると分かっているなら new HashMap<>(expectedSize / 0.75 + 1) でリハッシュを防げます。

Q: HashMap と Hashtable の違いは?
A: Hashtable は古い同期版で、現在は使わない。スレッドセーフが必要なら ConcurrentHashMap を使うこと。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 記述方法
  2. コメント
  3. 変数の宣言
  4. 関数
  5. 演算子
  6. 条件文
  7. 配列
  8. 連想配列
  9. ループ処理
  10. 非同期処理
  11. 同期処理
  12. 確認ウィンドウを表示する方法
  13. 文字の置換
  14. base urlを取得する方法
  15. formのsubmit前にjavascriptを呼び出す方法
  16. undefinedのイコール判定
  17. Javascript のみで form を post で submit する方法

最近更新/作成されたページ