この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:guest
更新日時:2026-06-11 07:12:00

タイトル: 連想配列
SEOタイトル: Java Map (連想配列) 完全ガイド

この記事の要点
  • Java の連想配列は Map<K, V> インタフェース。実装は HashMap / LinkedHashMap / TreeMap / ConcurrentHashMap
  • HashMap: 順序なし、最速。一般用途
  • LinkedHashMap: 挿入順を保持。LRU キャッシュにも
  • TreeMap: キーでソート済み。範囲検索 (subMap) 可
  • ConcurrentHashMap: スレッドセーフ。並列性高い
  • Java 9+ Map.of(&quot;a&quot;, 1, &quot;b&quot;, 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
values()値の CollectionCollection
entrySet()エントリの SetSet>
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 を使うこと。