Java で List<独自クラス> を並び替えるケースは、業務システムでも Web アプリでも頻繁に登場します。
「番号順」「日付順」「名称順」といった順序を付けたい場合、Comparator を使ったソートが基本となります。
この記事では、従来の Comparator 実装から、Java 8 以降で推奨されるラムダ式・メソッド参照、複数キーによるソート、null の扱いまで網羅的に解説します。
■ 前提:ソート対象となる独自クラス
サンプルとして、番号とデータ文字列を持つシンプルなクラス SampleBean を利用します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class SampleBean { private Integer no; private String data; public SampleBean(Integer no, String data) { this.no = no; this.data = data; } public Integer getNo() { return no; } public String getData() { return data; } @Override public String toString() { return "SampleBean{no=" + no + ", data='" + data + "'}"; } } |
■ 1. 従来方式:Comparator を使った基本的なソート
Java 7 以前から使われている、もっともオーソドックスな方法です。
● 昇順(data 順)
|
1 2 3 4 5 6 7 8 9 10 11 |
Collections.sort(list, new Comparator<SampleBean>() { @Override public int compare(SampleBean o1, SampleBean o2) { if (o1 == null && o2 == null) return 0; if (o1 == null) return 1; if (o2 == null) return -1; return o1.getData().compareTo(o2.getData()); } }); |
● 降順(data 順)
|
1 2 3 4 5 6 7 8 9 10 11 |
Collections.sort(list, new Comparator<SampleBean>() { @Override public int compare(SampleBean o1, SampleBean o2) { if (o1 == null && o2 == null) return 0; if (o1 == null) return 1; if (o2 == null) return -1; return o2.getData().compareTo(o1.getData()); } }); |
この書き方は問題なく動作しますが、コード量が多くなりがちです。
■ 2. Java 8 以降のモダンな書き方(ラムダ式 / メソッド参照)
Java 8 以降は List#sort() と Comparator.comparing() を組み合わせる方法が推奨です。
● 昇順(data 順)
|
1 2 3 4 5 6 7 8 |
list.sort( Comparator.comparing( SampleBean::getData, Comparator.nullsLast(String::compareTo) ) ); |
● 降順(data 順)
|
1 2 3 4 5 6 7 8 |
list.sort( Comparator.comparing( SampleBean::getData, Comparator.nullsLast(String::compareTo) ).reversed() ); |
null を含むリストにも安全に対応できます。
List.sort() を使うと Collections.sort() より自然な書き方になります。
■ 3. 複数項目でソート(no → data の順)
複数の基準で順序付けしたい場合は thenComparing() を使います。
|
1 2 3 4 5 6 7 |
// 昇順:no → data list.sort( Comparator.comparingInt(SampleBean::getNo) .thenComparing(SampleBean::getData, Comparator.nullsLast(String::compareTo)) ); |
非常に読みやすく、業務アプリで多い「複数キーのソート」に最適です。
■ 4. Stream を使って別リストとして返したい場合
元のリストを変更したくない場合は Stream#sorted() が便利です。
|
1 2 3 4 5 6 7 8 9 10 11 |
List<SampleBean> sortedList = list.stream() .filter(Objects::nonNull) .sorted( Comparator.comparing( SampleBean::getData, Comparator.nullsLast(String::compareTo) ) ) .collect(Collectors.toList()); |
元リストを変更せず、新しいソート済みリストを取得できます。
■ 5. 昇順/降順を切り替えられるユーティリティ例
ソートロジックを何度も書きたくない場合はユーティリティ化がおすすめです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public static List<SampleBean> sortByData(List<SampleBean> list, boolean ascending) { if (list == null) return Collections.emptyList(); Comparator<SampleBean> comp = Comparator.comparing( SampleBean::getData, Comparator.nullsLast(String::compareTo) ); if (!ascending) { comp = comp.reversed(); } list.sort(comp); return list; } |
呼び出し側は極めてシンプルになります。
■ 6. ソート時の注意点・ベストプラクティス
- null の扱いを明確にする
→Comparator.nullsFirst/nullsLastを必ず指定する。 - 数値差分で比較しない
→o1.getNo() - o2.getNo()はオーバーフローのリスク。
→Comparator.comparingInt()を使う。 - String.compareTo() のロケール問題
日本語の読み順でソートしたい場合はCollatorを使用。
|
1 2 3 4 |
Collator collator = Collator.getInstance(Locale.JAPANESE); list.sort(Comparator.comparing(SampleBean::getData, collator)); |
- 不変リスト(List.of など)はソート不可
→ 新しくリストを作ってソートする必要あり。
■ まとめ
独自クラスのリストソートは、Java 8 以降の Comparator.comparing() を使うことで大幅に簡潔に記述できます。
昇順・降順の切り替え、複数キーのソート、null の扱いなどを適切に設計すれば、より安全で読みやすいソースコードを維持できます。
業務で頻出する処理なので、ぜひプロジェクトに最適な形で活用してみてください。
