導入: なぜ複合ソートが重要なのか
業務システムにおいて、「まず姓で並び替え、同じ姓なら名で並び替える」といった複数条件のソートは頻出の要件です。従来、Javaでは複雑なif文やネストされた比較ロジックを記述する必要がありましたが、Java 8以降で導入されたComparatorインターフェースのthenComparingメソッドを使うことで、非常に簡潔かつ可読性の高いコードでこれを実現できます。複雑な条件を「宣言的」に書けることは、保守性を高めるために非常に重要です。
基礎知識: ComparatorとthenComparingの仕組み
JavaのComparatorは、オブジェクトの比較ルールを定義する関数型インターフェースです。thenComparingは、既存のComparatorの結果が「等しい(0を返す)」場合にのみ、次の比較ロジックを実行する「連鎖(チェイン)」機能を提供します。これにより、第一優先、第二優先、といった順序付けを直感的に繋げることができます。
実装/解決策: 複合ソートの手順
1. メインとなるソート基準(Comparator.comparing)を定義する。
2. 続いてソートしたい基準をthenComparingで連結する。
3. 必要に応じてreversed()で降順にするなど、柔軟な組み合わせを行う。
サンプルプログラム: 従業員リストの複合ソート
以下のコードは、従業員リストを「部署IDの昇順」、次に「給与の降順」でソートする例です。
import java.util.;
import java.util.stream.Collectors;
public class SortExample {
// 従業員クラス
static class Employee {
String name;
int deptId;
int salary;
Employee(String name, int deptId, int salary) {
this.name = name;
this.deptId = deptId;
this.salary = salary;
}
}
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("佐藤", 1, 300),
new Employee("鈴木", 2, 500),
new Employee("田中", 1, 400)
);
// 複合ソートの実行
// 1. 部署ID(deptId)で昇順
// 2. 同じ部署内では給与(salary)で降順
List<Employee> sorted = employees.stream()
.sorted(Comparator.comparingInt(Employee::getDeptId)
.thenComparing(Comparator.comparingInt(Employee::getSalary).reversed()))
.collect(Collectors.toList());
sorted.forEach(e -> System.out.println(e.name + ": 部署" + e.deptId + ", 給与" + e.salary));
}
}
応用・注意点: 現場での運用Tips
null値への対応: データにnullが含まれる場合、単純な比較はNullPointerExceptionを引き起こします。比較を行う際は、Comparator.nullsFirst(Comparator)やnullsLast(Comparator)を併用することを推奨します。
パフォーマンスの考慮: 大規模なリストをソートする場合、Comparatorの生成コスト自体は微小ですが、比較ロジック内で重いメソッドを呼び出さないよう注意してください。また、基本データ型(int, long, double)には、comparingIntやcomparingDoubleといった専用メソッドを使うことで、オートボクシングを避けてパフォーマンスを最適化できます。現場では、可読性と性能のバランスを考慮し、ラムダ式を適宜メソッド参照に置き換えるのがシニアの流儀です。

コメント