Java : Comparator (比較) - API使用例
Comparator (Java SE 21 & JDK 21) の使い方まとめです。
 だいたいのメソッドを網羅済みです。
 API仕様書のおともにどうぞ。
概要
オブジェクトのコレクションで全体順序付けを行う比較関数です。 コンパレータ(Comparator)をソート・メソッド(Collections.sortやArrays.sortなど)に渡すと、ソート順を正確に制御できます。
Comparator は、2つのオブジェクトの大小比較を実装するための関数型インタフェースです。
 主に、Collections.sort や Arrays.sort、Stream.sorted などのソートに使われます。
似た API に、Comparable インタフェースがあります。
 Comparable は実装したクラス自体が比較可能になるのに対して、Comparator は比較対象となるクラスに手を加える必要はありません。
以下は Comparator のコード例となります。
record Sample(String text, int num) {
    @Override
    public String toString() {
        return "(%s, %d)".formatted(text, num);
    }
}
final var comparator = new Comparator<Sample>() {
    @Override
    public int compare(Sample o1, Sample o2) {
        if (!o1.text().equals(o2.text())) {
            return o1.text().compareTo(o2.text());
        } else {
            return o1.num() - o2.num();
        }
    }
};
final var list = List.of(
        new Sample("bbb", 2), new Sample("bbb", 3), new Sample("bbb", 1),
        new Sample("aaa", 3), new Sample("aaa", 1), new Sample("aaa", 2)
);
// [(bbb, 2), (bbb, 3), (bbb, 1), (aaa, 3), (aaa, 1), (aaa, 2)]
System.out.println(list);
final var sorted = list.stream().sorted(comparator).toList();
// [(aaa, 1), (aaa, 2), (aaa, 3), (bbb, 1), (bbb, 2), (bbb, 3)]
System.out.println(sorted);
 
メソッド
int compare (T o1, T o2)
順序付けのために2つの引数を比較します。
record Sample(String text, int num) {
    @Override
    public String toString() {
        return "(%s, %d)".formatted(text, num);
    }
}
final var comparator = new Comparator<Sample>() {
    @Override
    public int compare(Sample o1, Sample o2) {
        if (!o1.text().equals(o2.text())) {
            return o1.text().compareTo(o2.text());
        } else {
            return o1.num() - o2.num();
        }
    }
};
final var list = List.of(
        new Sample("bbb", 2), new Sample("bbb", 3), new Sample("bbb", 1),
        new Sample("aaa", 3), new Sample("aaa", 1), new Sample("aaa", 2)
);
// [(bbb, 2), (bbb, 3), (bbb, 1), (aaa, 3), (aaa, 1), (aaa, 2)]
System.out.println(list);
final var sorted = list.stream().sorted(comparator).toList();
// [(aaa, 1), (aaa, 2), (aaa, 3), (bbb, 1), (bbb, 2), (bbb, 3)]
System.out.println(sorted);
 
      static <T, U extends Comparable<? super U>> Comparator<T> comparing (Function<? super T,? extends U> keyExtractor)
型TからComparableソート・キーを抽出する関数を受け取り、そのソート・キーで比較する Comparator<T>を返します。
record Sample(String textA, String textB) {
    @Override
    public String toString() {
        return "(%s, %s)".formatted(textA, textB);
    }
}
final var list = List.of(
        new Sample("ddd", "W"), new Sample("ccc", "X"),
        new Sample("bbb", "Z"), new Sample("aaa", "Y")
);
// [(ddd, W), (ccc, X), (bbb, Z), (aaa, Y)]
System.out.println(list);
{
    final var keyExtractor = new Function<Sample, String>() {
        @Override
        public String apply(Sample sample) {
            return sample.textA();
        }
    };
    final var comparator = Comparator.comparing(keyExtractor);
    final var sorted = list.stream().sorted(comparator).toList();
    // [(aaa, Y), (bbb, Z), (ccc, X), (ddd, W)]
    System.out.println(sorted);
}
{
    final var comparator = Comparator.comparing(Sample::textB);
    final var sorted = list.stream().sorted(comparator).toList();
    // [(ddd, W), (ccc, X), (aaa, Y), (bbb, Z)]
    System.out.println(sorted);
}
 
      static <T, U> Comparator<T> comparing (Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator)
型Tからソート・キーを抽出する関数を受け取り、指定されたComparatorを使ってそのソート・キーで比較するComparator<T>を返します。
record Sample(String text, int num) {
    @Override
    public String toString() {
        return "(%s, %d)".formatted(text, num);
    }
}
final var list = List.of(
        new Sample("ccc", 2), new Sample("DDD", 1),
        new Sample("aaa", 4), new Sample("BBB", 3)
);
// [(ccc, 2), (DDD, 1), (aaa, 4), (BBB, 3)]
System.out.println(list);
{
    // 大文字・小文字区別なし。
    final var comparator = Comparator.comparing(
            Sample::text, String.CASE_INSENSITIVE_ORDER);
    final var sorted = list.stream().sorted(comparator).toList();
    // [(aaa, 4), (BBB, 3), (ccc, 2), (DDD, 1)]
    System.out.println(sorted);
}
{
    // 大文字・小文字区別あり。
    final var comparator = Comparator.comparing(Sample::text);
    final var sorted = list.stream().sorted(comparator).toList();
    // [(BBB, 3), (DDD, 1), (aaa, 4), (ccc, 2)]
    System.out.println(sorted);
}
 
      static <T> Comparator<T> comparingDouble (ToDoubleFunction<? super T> keyExtractor)
型Tからdoubleソート・キーを抽出する関数を受け取り、そのソート・キーで比較するComparator<T>を返します。
record Sample(double num, String text) {
    @Override
    public String toString() {
        return "(%.1f, %s)".formatted(num, text);
    }
}
final var list = List.of(
        new Sample(2.0, "ccc"), new Sample(3.0, "bbb"),
        new Sample(4.0, "aaa"), new Sample(1.0, "ddd")
);
// [(2.0, ccc), (3.0, bbb), (4.0, aaa), (1.0, ddd)]
System.out.println(list);
final var comparator = Comparator.comparingDouble(Sample::num);
final var sorted = list.stream().sorted(comparator).toList();
// [(1.0, ddd), (2.0, ccc), (3.0, bbb), (4.0, aaa)]
System.out.println(sorted);
 
      static <T> Comparator<T> comparingInt (ToIntFunction<? super T> keyExtractor)
型Tからintソート・キーを抽出する関数を受け取り、そのソート・キーで比較するComparator<T>を返します。
record Sample(int num, String text) {
    @Override
    public String toString() {
        return "(%d, %s)".formatted(num, text);
    }
}
final var list = List.of(
        new Sample(2, "ccc"), new Sample(3, "bbb"),
        new Sample(4, "aaa"), new Sample(1, "ddd")
);
// [(2, ccc), (3, bbb), (4, aaa), (1, ddd)]
System.out.println(list);
final var comparator = Comparator.comparingInt(Sample::num);
final var sorted = list.stream().sorted(comparator).toList();
// [(1, ddd), (2, ccc), (3, bbb), (4, aaa)]
System.out.println(sorted);
 
      static <T> Comparator<T> comparingLong (ToLongFunction<? super T> keyExtractor)
型Tからlongソート・キーを抽出する関数を受け取り、そのソート・キーで比較するComparator<T>を返します。
record Sample(long num, String text) {
    @Override
    public String toString() {
        return "(%d, %s)".formatted(num, text);
    }
}
final var list = List.of(
        new Sample(20000000000L, "ccc"), new Sample(30000000000L, "bbb"),
        new Sample(40000000000L, "aaa"), new Sample(10000000000L, "ddd")
);
// [(20000000000, ccc), (30000000000, bbb), (40000000000, aaa), (10000000000, ddd)]
System.out.println(list);
final var comparator = Comparator.comparingLong(Sample::num);
final var sorted = list.stream().sorted(comparator).toList();
// [(10000000000, ddd), (20000000000, ccc), (30000000000, bbb), (40000000000, aaa)]
System.out.println(sorted);
 
      boolean equals (Object obj)
ほかのオブジェクトがこのコンパレータと「等しい」かどうかを示します。
final var comparator1 = Comparator.<String>naturalOrder();
final var comparator2 = Comparator.<String>reverseOrder();
System.out.println(comparator1.equals(comparator2)); // false
 
      static <T extends Comparable<? super T>> Comparator<T> naturalOrder ()
Comparableオブジェクトを自然順序で比較するコンパレータを返します。
final var comparator = Comparator.<String>naturalOrder();
final var list = List.of("ccc", "ddd", "aaa", "bbb");
final var sorted = list.stream().sorted(comparator).toList();
System.out.println(list); // [ccc, ddd, aaa, bbb]
System.out.println(sorted); // [aaa, bbb, ccc, ddd]
 
      static <T> Comparator<T> nullsFirst (Comparator<? super T> comparator)
nullをnull以外より小さいとみなす、nullフレンドリのコンパレータを返します。
final var list = new ArrayList<String>();
list.add("ccc");
list.add("ddd");
list.add(null);
list.add("aaa");
list.add("bbb");
// [ccc, ddd, null, aaa, bbb]
System.out.println(list);
final var comparator = Comparator.<String>naturalOrder();
{
    final var nullsFirstComparator = Comparator.nullsFirst(comparator);
    final var sorted = list.stream().sorted(nullsFirstComparator).toList();
    // [null, aaa, bbb, ccc, ddd]
    System.out.println(sorted);
}
{
    final var nullsLastComparator = Comparator.nullsLast(comparator);
    final var sorted = list.stream().sorted(nullsLastComparator).toList();
    // [aaa, bbb, ccc, ddd, null]
    System.out.println(sorted);
}
{
    try {
        final var sorted = list.stream().sorted(comparator).toList();
    } catch (NullPointerException e) {
        System.out.println("NullPointerException!");
    }
    // 結果
    // ↓
    //NullPointerException!
}
 
      static <T> Comparator<T> nullsLast (Comparator<? super T> comparator)
nullをnull以外より大きいとみなす、nullフレンドリのコンパレータを返します。
final var list = new ArrayList<String>();
list.add("ccc");
list.add("ddd");
list.add(null);
list.add("aaa");
list.add("bbb");
// [ccc, ddd, null, aaa, bbb]
System.out.println(list);
final var comparator = Comparator.<String>naturalOrder();
{
    final var nullsFirstComparator = Comparator.nullsFirst(comparator);
    final var sorted = list.stream().sorted(nullsFirstComparator).toList();
    // [null, aaa, bbb, ccc, ddd]
    System.out.println(sorted);
}
{
    final var nullsLastComparator = Comparator.nullsLast(comparator);
    final var sorted = list.stream().sorted(nullsLastComparator).toList();
    // [aaa, bbb, ccc, ddd, null]
    System.out.println(sorted);
}
{
    try {
        final var sorted = list.stream().sorted(comparator).toList();
    } catch (NullPointerException e) {
        System.out.println("NullPointerException!");
    }
    // 結果
    // ↓
    //NullPointerException!
}
 
      default Comparator<T> reversed ()
このコンパレータの逆順を義務付けるコンパレータを返します。
final var comparator = Comparator.<String>naturalOrder();
final var reversedComparator = comparator.reversed();
final var list = List.of("ccc", "ddd", "aaa", "bbb");
System.out.println(list); // [ccc, ddd, aaa, bbb]
final var sorted = list.stream().sorted(comparator).toList();
final var reversedSorted = list.stream().sorted(reversedComparator).toList();
System.out.println(sorted); // [aaa, bbb, ccc, ddd]
System.out.println(reversedSorted); // [ddd, ccc, bbb, aaa]
 
      static <T extends Comparable<? super T>> Comparator<T> reverseOrder ()
自然順序付けの逆を義務付けるコンパレータを返します。
final var comparator = Comparator.<String>reverseOrder();
final var list = List.of("ccc", "ddd", "aaa", "bbb");
final var sorted = list.stream().sorted(comparator).toList();
System.out.println(list); // [ccc, ddd, aaa, bbb]
System.out.println(sorted); // [ddd, ccc, bbb, aaa]
 
      default Comparator<T> thenComparing (Comparator<? super T> other)
辞書式順序コンパレータをもう一方のコンパレータとともに返します。
final var list = List.of("aaa", "bbb", "ccc", "b", "c", "a", "cc", "aa", "bb");
System.out.println(list); // [aaa, bbb, ccc, b, c, a, cc, aa, bb]
// 自然順序によるソート
final var naturalComparator = Comparator.<String>naturalOrder();
final var naturalSorted = list.stream().sorted(naturalComparator).toList();
System.out.println(naturalSorted); // [a, aa, aaa, b, bb, bbb, c, cc, ccc]
// 文字列の長さでソート
final var lengthComparator = Comparator.comparingInt(String::length);
final var lengthSorted = list.stream().sorted(lengthComparator).toList();
System.out.println(lengthSorted); // [b, c, a, cc, aa, bb, aaa, bbb, ccc]
// 文字列の長さ -> 自然順序でソート
final var comparator = lengthComparator.thenComparing(naturalComparator);
final var sorted = list.stream().sorted(comparator).toList();
System.out.println(sorted); // [a, b, c, aa, bb, cc, aaa, bbb, ccc]
 
      default <U extends Comparable<? super U>> Comparator<T> thenComparing (Function<? super T,? extends U> keyExtractor)
Comparableソート・キーを抽出する関数を含む辞書式順序コンパレータを返します。
record Sample(int num, String text) {
    @Override
    public String toString() {
        return "(%d, %s)".formatted(num, text);
    }
}
final var list = List.of(
        new Sample(2, "ccc"), new Sample(2, "bbb"), new Sample(2, "aaa"),
        new Sample(1, "bbb"), new Sample(1, "ccc"), new Sample(1, "aaa")
);
// [(2, ccc), (2, bbb), (2, aaa), (1, bbb), (1, ccc), (1, aaa)]
System.out.println(list);
final var numComparator = Comparator.comparing(Sample::num);
final var numSorted = list.stream().sorted(numComparator).toList();
// [(1, bbb), (1, ccc), (1, aaa), (2, ccc), (2, bbb), (2, aaa)]
System.out.println(numSorted);
final var comparator = numComparator.thenComparing(Sample::text);
final var sorted = list.stream().sorted(comparator).toList();
// [(1, aaa), (1, bbb), (1, ccc), (2, aaa), (2, bbb), (2, ccc)]
System.out.println(sorted);
 
      default <U> Comparator<T> thenComparing (Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator)
指定されたComparatorで比較されるキーを抽出する関数を含む辞書式順序コンパレータを返します。
record Sample(int num, String text) {
    @Override
    public String toString() {
        return "(%d, %s)".formatted(num, text);
    }
}
final var list = List.of(
        new Sample(2, "ccc"), new Sample(2, "BBB"), new Sample(2, "aaa"),
        new Sample(1, "bbb"), new Sample(1, "CCC"), new Sample(1, "AAA")
);
// [(2, ccc), (2, BBB), (2, aaa), (1, bbb), (1, CCC), (1, AAA)]
System.out.println(list);
final var numComparator = Comparator.comparing(Sample::num);
final var numSorted = list.stream().sorted(numComparator).toList();
// [(1, bbb), (1, CCC), (1, AAA), (2, ccc), (2, BBB), (2, aaa)]
System.out.println(numSorted);
final var comparator = numComparator.thenComparing(
        Sample::text, String.CASE_INSENSITIVE_ORDER);
final var sorted = list.stream().sorted(comparator).toList();
// [(1, AAA), (1, bbb), (1, CCC), (2, aaa), (2, BBB), (2, ccc)]
System.out.println(sorted);
 
      default Comparator<T> thenComparingDouble (ToDoubleFunction<? super T> keyExtractor)
doubleソート・キーを抽出する関数を含む辞書式順序コンパレータを返します。
record Sample(String text, double num) {
    @Override
    public String toString() {
        return "(%s, %.1f)".formatted(text, num);
    }
}
final var list = List.of(
        new Sample("bbb", 2.0), new Sample("bbb", 3.0), new Sample("bbb", 1.0),
        new Sample("aaa", 3.0), new Sample("aaa", 1.0), new Sample("aaa", 2.0)
);
// [(bbb, 2.0), (bbb, 3.0), (bbb, 1.0), (aaa, 3.0), (aaa, 1.0), (aaa, 2.0)]
System.out.println(list);
final var textComparator = Comparator.comparing(Sample::text);
final var textSorted = list.stream().sorted(textComparator).toList();
// [(aaa, 3.0), (aaa, 1.0), (aaa, 2.0), (bbb, 2.0), (bbb, 3.0), (bbb, 1.0)]
System.out.println(textSorted);
final var comparator = textComparator.thenComparingDouble(Sample::num);
final var sorted = list.stream().sorted(comparator).toList();
// [(aaa, 1.0), (aaa, 2.0), (aaa, 3.0), (bbb, 1.0), (bbb, 2.0), (bbb, 3.0)]
System.out.println(sorted);
 
      default Comparator<T> thenComparingInt (ToIntFunction<? super T> keyExtractor)
intソート・キーを抽出する関数を使用して、辞書順コンパレータを返します。
record Sample(String text, int num) {
    @Override
    public String toString() {
        return "(%s, %d)".formatted(text, num);
    }
}
final var list = List.of(
        new Sample("bbb", 2), new Sample("bbb", 3), new Sample("bbb", 1),
        new Sample("aaa", 3), new Sample("aaa", 1), new Sample("aaa", 2)
);
// [(bbb, 2), (bbb, 3), (bbb, 1), (aaa, 3), (aaa, 1), (aaa, 2)]
System.out.println(list);
final var textComparator = Comparator.comparing(Sample::text);
final var textSorted = list.stream().sorted(textComparator).toList();
// [(aaa, 3), (aaa, 1), (aaa, 2), (bbb, 2), (bbb, 3), (bbb, 1)]
System.out.println(textSorted);
final var comparator = textComparator.thenComparingInt(Sample::num);
final var sorted = list.stream().sorted(comparator).toList();
// [(aaa, 1), (aaa, 2), (aaa, 3), (bbb, 1), (bbb, 2), (bbb, 3)]
System.out.println(sorted);
 
      default Comparator<T> thenComparingLong (ToLongFunction<? super T> keyExtractor)
longソート・キーを抽出する関数を含む辞書式順序コンパレータを返します。
record Sample(String text, long num) {
    @Override
    public String toString() {
        return "(%s, %d)".formatted(text, num);
    }
}
final var list = List.of(
        new Sample("bbb", 2), new Sample("bbb", 3), new Sample("bbb", 1),
        new Sample("aaa", 3), new Sample("aaa", 1), new Sample("aaa", 2)
);
// [(bbb, 2), (bbb, 3), (bbb, 1), (aaa, 3), (aaa, 1), (aaa, 2)]
System.out.println(list);
final var textComparator = Comparator.comparing(Sample::text);
final var textSorted = list.stream().sorted(textComparator).toList();
// [(aaa, 3), (aaa, 1), (aaa, 2), (bbb, 2), (bbb, 3), (bbb, 1)]
System.out.println(textSorted);
final var comparator = textComparator.thenComparingLong(Sample::num);
final var sorted = list.stream().sorted(comparator).toList();
// [(aaa, 1), (aaa, 2), (aaa, 3), (bbb, 1), (bbb, 2), (bbb, 3)]
System.out.println(sorted);
 
      関連記事
- List(リスト)の基本
 - Map(マップ)の基本
 - Set(セット)の基本
 - List の初期化方法いろいろ
 - Map の初期化方法いろいろ
 - Set の初期化方法いろいろ
 - 配列 vs. List
 - 配列からListへの変換
 - List から配列への変換
 
- API 使用例 
        
- Collection (コレクション)
 - Collections (コレクション操作)
 - Comparable
 - Iterator
 - List (リスト)
 - Map (マップ)
 - Map.Entry (キーと値のペア)
 - Queue (キュー) 
          
- AbstractQueue
 - ArrayBlockingQueue (ブロッキング・配列キュー)
 - ArrayDeque (両端キュー)
 - BlockingDeque (ブロッキング・両端キュー)
 - BlockingQueue (ブロッキング・キュー)
 - ConcurrentLinkedDeque (並列処理用・両端キュー)
 - ConcurrentLinkedQueue (並列処理用キュー)
 - Deque (両端キュー)
 - LinkedBlockingDeque (ブロッキング・リンク両端キュー)
 - LinkedBlockingQueue (ブロッキング・リンクキュー)
 - LinkedList (二重リンク・リスト)
 - PriorityBlockingQueue (ブロッキング・優先度付きキュー)
 - PriorityQueue (優先度付きキュー)
 
 - RandomAccess
 - Set (セット)
 - Spliterator
 
 



