こんにちは、かつコーチです。
「Listをソートしたいけど、毎回forループでアルゴリズムを書くのは面倒」と感じたことはありませんか。
今回は、そんな定番処理をまとめて解決してくれるCollectionsユーティリティクラスの使い方を紹介します。
Collectionsとは?
コレクション操作をまとめた便利クラス
Collections(java.utilパッケージにある、コレクションを操作する静的メソッドを集めたユーティリティクラス)は、ソート・検索・最大最小値の取得・不変化など、Listや Setに対するよくある処理をまとめて提供してくれます。
名前が似ているCollection(単数形、ListやSetの親インターフェース)とは別物なので、混同しないよう注意してください。
なぜ自前で実装せずCollectionsを使うべきか
ソートや二分探索は自分で実装しようとするとバグが混入しやすい処理です。Collectionsが提供するメソッドは標準ライブラリとして十分にテストされているため、車輪の再発明を避けて品質と可読性を両立できます。
基本の書き方
sort:Listを並び替える
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
List<Integer> numbers = new ArrayList<>(List.of(5, 3, 8, 1, 9));
Collections.sort(numbers);
System.out.println(numbers); // [1, 3, 5, 8, 9]
// 降順にしたい場合はComparatorを渡す
Collections.sort(numbers, Collections.reverseOrder());
System.out.println(numbers); // [9, 8, 5, 3, 1]
Collections.sortは内部的にListのsortメソッドを呼んでいるだけですが、reverseOrder()のような補助的なComparatorが用意されているのが便利な点です。
max・min:最大値・最小値を取得する
List<Integer> scores = List.of(72, 88, 45, 91, 60);
int max = Collections.max(scores);
int min = Collections.min(scores);
System.out.println("最高点: " + max); // 最高点: 91
System.out.println("最低点: " + min); // 最低点: 45
ループを書かずに1行で最大値・最小値を取れるので、集計処理を簡潔に書けます。
shuffle・reverse:順番を変える
List<String> names = new ArrayList<>(List.of("Aさん", "Bさん", "Cさん", "Dさん"));
Collections.shuffle(names); // ランダムに並び替える
System.out.println(names);
Collections.reverse(names); // 現在の順番を逆にする
System.out.println(names);
shuffleは抽選やランダム出題機能を作るときに重宝します。
unmodifiableList:変更不可なListに変換する
List<String> original = new ArrayList<>(List.of("A", "B", "C"));
List<String> readOnly = Collections.unmodifiableList(original);
readOnly.add("D"); // 実行時エラー
外部に渡すListを勝手に変更されたくない場合、unmodifiableListでラップして防御的に公開するのが定番のテクニックです。
よくあるつまずきポイント・エラー対処
Arrays.asListをsortしようとしてエラーになった
私が実際にハマった一次情報として、Arrays.asListで作ったListに要素を追加しようとして実行時エラーになったことがあります。
(sortは可能ですが、要素数を変える操作は不可という点でつまずきました)
// ❌Before:Arrays.asListが返すListはサイズ変更不可という点を見落とす
List<String> fixedList = java.util.Arrays.asList("C", "A", "B");
Collections.sort(fixedList); // これは成功する
fixedList.add("D"); // ここで実行時エラー
このコードを実行すると、次の例外が発生します。
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.add(AbstractList.java:153)
Arrays.asListが返すListは、内部の配列サイズを固定したままListのインターフェースでラップしたものです。
そのため要素の書き換え(set)はできても、追加・削除で要素数を変える操作はできません。Collections.unmodifiableListとは違い「一部の変更だけ許されている」中途半端な状態なので、初見だと混乱しやすいポイントです。
// ✅After:可変にしたいならnew ArrayList<>()で包み直す
List<String> mutableList = new ArrayList<>(java.util.Arrays.asList("C", "A", "B"));
Collections.sort(mutableList);
mutableList.add("D"); // 問題なく成功する
System.out.println(mutableList); // [A, B, C, D]
「Arrays.asListやList.ofで作ったListは、要素数を変える前提の処理には使わない」と覚えておくと、この手のエラーを未然に防げます。
応用・一歩先の使い方
synchronizedListでスレッドセーフにする
複数スレッドから同じListにアクセスする可能性がある場合、Collections.synchronizedListでラップすると同期処理付きのListに変換できます。
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
synchronized (syncList) {
for (String item : syncList) {
System.out.println(item);
}
}
ただし、走査(イテレーション)中は明示的なsynchronizedブロックが必要な点に注意してください。
より高度な並行処理が必要な場合は、java.util.concurrentパッケージのCopyOnWriteArrayListなどの検討もおすすめです。
emptyList・singletonListで軽量なListを作る
要素が0件・1件だけのListを返したい場合、専用のファクトリメソッドを使うとメモリ効率が良くなります。
List<String> empty = Collections.emptyList();
List<String> single = Collections.singletonList("唯一の要素");
System.out.println(empty); // []
System.out.println(single); // [唯一の要素]
「空のリストを返すメソッド」を書くときにnew ArrayList<>()ではなくCollections.emptyList()を使うと、意図が明確になり読み手にも伝わりやすくなります。
まとめ
この記事のポイント
- Collectionsはソート・検索・最大最小・不変化などをまとめて提供するユーティリティクラス
sort・max・min・shuffle・reverseで定番の操作を1行で書けるunmodifiableListで外部公開用の変更不可Listを作れるArrays.asListはサイズ変更不可な点に注意し、可変にしたいならnew ArrayList<>()で包み直す- 複数スレッドで扱うなら
synchronizedListやjava.util.concurrentの利用を検討する
次に読むべき記事
- Deque・Stack・Queueの使い方
- Listの基本:ArrayListとLinkedListの違い
タグ: Java, 中級者向け, コレクション