タイトル: コレクション(List,Set,Queue)
SEOタイトル: Java コレクション (List/Set/Queue/Map) 完全ガイド - 計算量と使い分け
| この記事の要点 |
- Java Collection Framework は List / Set / Queue / Map の 4 系統。すべて
java.util - List: 順序あり・重複あり (
ArrayList / LinkedList / Vector) - Set: 重複なし (
HashSet / TreeSet / LinkedHashSet) - Queue/Deque: FIFO/LIFO (
ArrayDeque / LinkedList / PriorityQueue) - Map: キー→値 (
HashMap / TreeMap / LinkedHashMap / ConcurrentHashMap)
|
Java Collection Framework 全体図
Iterable
└── Collection
├── List (順序あり・重複OK)
│ ├── ArrayList ★ 主力
│ ├── LinkedList (Queue/Deque も実装)
│ └── Vector (スレッドセーフ、古い)
│
├── Set (重複NG)
│ ├── HashSet ★ 主力
│ ├── LinkedHashSet (挿入順保持)
│ └── TreeSet (ソート済 - SortedSet)
│
└── Queue / Deque (FIFO/LIFO)
├── ArrayDeque ★ 主力 (Stack/Queue)
├── LinkedList
└── PriorityQueue (優先度キュー)
Map (Collection ではない別系統)
├── HashMap ★ 主力
├── LinkedHashMap (挿入順保持)
├── TreeMap (ソート済)
├── Hashtable (古い・非推奨)
└── ConcurrentHashMap (スレッドセーフ)
List の使い分け
| 実装 | get(i) | add 末尾 | add(0) | remove(0) | 備考 |
ArrayList | O(1) | O(1) ※ | O(n) | O(n) | ★ 通常はこれ。内部は配列 |
LinkedList | O(n) | O(1) | O(1) | O(1) | 双方向連結。両端操作多用なら |
Vector | O(1) | O(1) ※ | O(n) | O(n) | 古い・同期版。代わりに Collections.synchronizedList |
※ ArrayList の末尾追加は均し O(1) (配列拡張時のみ O(n))。
import java.util.*;
List list = new ArrayList<>();
list.add("A");
list.add("B");
list.add(0, "Z"); // 先頭挿入 (遅い)
list.get(0); // "Z"
list.remove("A");
list.size();
list.contains("B");
// 不変リスト (Java 9+)
List immut = List.of("a", "b", "c");
// immut.add("d"); // UnsupportedOperationException
// 既存リストから不変リスト
List copy = List.copyOf(list);
// for-each (Iterable)
for (String s : list) {
System.out.println(s);
}
// ソート
Collections.sort(list); // 自然順序
list.sort(Comparator.reverseOrder()); // 逆順
list.sort(Comparator.comparing(String::length));
Set の使い分け
| 実装 | add/contains | 順序 | 備考 |
HashSet | O(1) 平均 | なし | ★ 通常はこれ |
LinkedHashSet | O(1) 平均 | 挿入順 | イテレート順を保ちたいとき |
TreeSet | O(log n) | ソート済 | 範囲検索 (subSet 等) や first() が必要なとき |
Set set = new HashSet<>();
set.add(1); set.add(2); set.add(1); // 重複は無視
set.size(); // 2
// 重複排除に便利
List raw = List.of(1, 2, 2, 3, 3, 3);
Set unique = new HashSet<>(raw); // [1, 2, 3]
// TreeSet で範囲検索
NavigableSet tree = new TreeSet<>(List.of(1, 3, 5, 7, 9));
tree.first(); // 1
tree.last(); // 9
tree.higher(4); // 5 (4 より大きい最小)
tree.subSet(3, 8); // [3, 5, 7]
// 不変 Set (Java 9+)
Set immut = Set.of("a", "b", "c");
Queue / Deque の使い分け
| 実装 | 用途 | 備考 |
ArrayDeque | Stack / Queue / Deque | ★ Stack の代替推奨。null 不可 |
LinkedList | Queue / Deque | List も兼ねるが性能は ArrayDeque に劣る |
PriorityQueue | 優先度付きキュー | ヒープ。poll() で最小値取得 |
LinkedBlockingQueue | 並行プログラミング | java.util.concurrent |
// Stack (LIFO) - 古い Stack クラスより ArrayDeque 推奨
Deque stack = new ArrayDeque<>();
stack.push(1); stack.push(2); stack.push(3);
stack.pop(); // 3
stack.peek(); // 2
// Queue (FIFO)
Queue queue = new ArrayDeque<>();
queue.offer("A");
queue.offer("B");
queue.poll(); // "A"
// 優先度キュー (小さい順)
PriorityQueue pq = new PriorityQueue<>();
pq.offer(5); pq.offer(1); pq.offer(3);
pq.poll(); // 1
pq.poll(); // 3
// 大きい順
PriorityQueue pqDesc = new PriorityQueue<>(Comparator.reverseOrder());
Map の使い分け
| 実装 | get/put | 順序 | 備考 |
HashMap | O(1) 平均 | なし | ★ 通常はこれ。null キー/値 OK |
LinkedHashMap | O(1) | 挿入順 or アクセス順 | LRU キャッシュにも使える |
TreeMap | O(log n) | キーソート | 範囲検索可 |
ConcurrentHashMap | O(1) 平均 | なし | ★ スレッドセーフ。マルチスレッド推奨 |
Hashtable | O(1) | なし | 古い。同期だが性能悪い。使わない |
Map map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.get("a"); // 1
map.getOrDefault("z", 0); // 0
map.containsKey("a"); // true
map.putIfAbsent("c", 3); // 既存なら無視
// 集計イディオム
Map count = new HashMap<>();
for (String word : words) {
count.merge(word, 1, Integer::sum);
}
// イテレート
for (Map.Entry e : map.entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue());
}
// 不変 Map (Java 9+)
Map immut = Map.of("a", 1, "b", 2);
Map big = Map.ofEntries(
Map.entry("a", 1),
Map.entry("b", 2)
);
// LRU キャッシュ
Map lru = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > 100;
}
};
Stream API (Java 8+)
import java.util.stream.*;
List nums = List.of(1, 2, 3, 4, 5);
// フィルタ + マップ + 集計
int sum = nums.stream()
.filter(n -> n % 2 == 0)
.mapToInt(Integer::intValue)
.sum(); // 6
// グルーピング
Map> grouped = nums.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// List → Map
Map userMap = users.stream()
.collect(Collectors.toMap(User::getId, u -> u));
// 並列ストリーム
nums.parallelStream().forEach(System.out::println);
スレッドセーフ化
// 1. 古い同期ラッパ (性能低い・推奨されない)
List syncList = Collections.synchronizedList(new ArrayList<>());
Map syncMap = Collections.synchronizedMap(new HashMap<>());
// 2. ★ 推奨: java.util.concurrent
import java.util.concurrent.*;
ConcurrentHashMap cmap = new ConcurrentHashMap<>();
CopyOnWriteArrayList cowList = new CopyOnWriteArrayList<>();
ConcurrentLinkedQueue cqueue = new ConcurrentLinkedQueue<>();
// 3. ブロッキングキュー (プロデューサ/コンシューマ)
BlockingQueue queue = new LinkedBlockingQueue<>();
queue.put(task); // 満杯ならブロック
Task t = queue.take(); // 空ならブロック
選び方フローチャート
- キーで値を引きたい →
HashMap。順序が必要なら LinkedHashMap or TreeMap。並行なら ConcurrentHashMap
- 重複を排除したい →
HashSet。順序保持なら LinkedHashSet。ソートなら TreeSet
- 順序ある可変リスト →
ArrayList。先頭挿入頻発なら LinkedList
- Stack / Queue →
ArrayDeque (Stack クラスや LinkedList より速い)
- 優先度順に取り出す →
PriorityQueue
- 読み取り専用 →
List.of / Set.of / Map.of (Java 9+)
FAQ
Q: HashMap で順序を保ちたい
A: LinkedHashMap を使ってください。HashMap はハッシュ値順なので順序保証ゼロです。
Q: HashSet が遅い。なぜ?
A: 格納する要素の hashCode() / equals() 実装が悪いと衝突が増えて O(n) に劣化します。Java 8+ は衝突過多時に内部を木構造化しますが、根本は hashCode を直すこと。
Q: List → 配列に変換したい
A: String[] arr = list.toArray(new String[0]);。Java 11+ は list.toArray(String[]::new)。