Java | Java 標準ライブラリ:Collections.sort

Java Java
スポンサーリンク

Collections.sort の役割をざっくりつかむ

Collections.sort は、

List の中身を並び替えるための標準メソッド」

です。

配列には Arrays.sort がありましたよね。
その「List 版」が Collections.sort だと思ってください。

やってくれることはシンプルで、

List の中身を、その場で並び替える(破壊的ソート)

です。

Collections.sort(list); と呼ぶと、list の中身そのものが並び替えられ、
新しい List が返ってくるわけではありません。


一番基本:List<Integer> / List<String> を昇順にソートする

整数の List を昇順にソート

まずは一番シンプルな例から。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SortIntBasic {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(30);
        numbers.add(10);
        numbers.add(20);

        System.out.println("ソート前: " + numbers); // [30, 10, 20]

        Collections.sort(numbers);                 // 昇順に並び替え

        System.out.println("ソート後: " + numbers); // [10, 20, 30]
    }
}
Java

Integer は「自然な順序」として「小さい順」が決まっているので、
Collections.sort(numbers); と書くだけで、昇順に並びます。

ここで重要なのは、

戻り値を受け取っていない
numbers 自体の中身が書き換えられている

という点です。

文字列の List を辞書順にソート

String も自然な順序(辞書順)を持っています。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SortStringBasic {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Bob");
        names.add("Alice");
        names.add("Carol");

        Collections.sort(names);

        System.out.println(names); // [Alice, Bob, Carol]
    }
}
Java

英字の場合は A → Z の順(辞書順)になります。
日本語も、StringcompareTo のルールに従って一応ソートされますが、
本格的な「五十音順」などをやろうとすると Collator など他の仕組みが必要になってきます。


Collections.sort が使える条件:要素が Comparable であること

T extends Comparable<? super T> って何を言っているのか

Collections.sort(List<T> list) のシグネチャ(定義)は、ざっくりこうなっています。

public static <T extends Comparable<? super T>> void sort(List<T> list)
Java

この「T extends Comparable<? super T>」が少し難しく見えますが、
要するに

list に入っている要素 T は、Comparable(compareTo で比較可能)でなければならない」

と言っているだけです。

Integer, String, LocalDate など、多くの標準クラスは Comparable を実装しているので、そのまま Collections.sort にかけられます。

自作クラスを sort しようとして怒られるパターン

自分で作ったクラスを List に入れてそのままソートすると、コンパイルエラーになります。

class User {
    String name;
    int age;

    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
Java
List<User> users = new ArrayList<>();
users.add(new User("Alice", 30));
users.add(new User("Bob", 20));

Collections.sort(users);  // コンパイルエラー:User は Comparable じゃない
Java

UserComparable<User> を実装していないので、
「どういう順番で並べていいか分からない」と怒られているわけです。

このときの選択肢は 2 つです。

クラスに自然な順序(標準の並び)を Comparable で埋め込む
ソート時に Comparator を渡して、「この場面の並べ方」を教える

この 2 本柱が、Collections.sort を理解するうえでのカギになります。


パターン1:Comparable を実装して自然な順序で sort する

User を「年齢昇順」で並べる自然順序にする

Comparable<User> を実装してみます。

class User implements Comparable<User> {
    String name;
    int age;

    User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(User other) {
        return Integer.compare(this.age, other.age); // 年齢昇順
    }

    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
}
Java

これで、Collections.sort(users) がそのまま動くようになります。

import java.util.*;

public class SortUserComparable {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        users.add(new User("Alice", 30));
        users.add(new User("Bob",   20));
        users.add(new User("Carol", 25));

        Collections.sort(users);  // User.compareTo が使われる

        System.out.println(users); // [Bob(20), Carol(25), Alice(30)]
    }
}
Java

Collections.sort 側から見ると、

「要素が Comparable なら、その compareTo に従って並べればいい」

だけです。

Comparable を実装することで、
「User の標準の並び順=年齢昇順」という“自然順序”が決まりました。


パターン2:Comparator を渡して「場面ごとの並べ方」を指定する

自然順序とは違う並べ方をしたい場合

例えば、さっきの User の自然順序は「年齢昇順」になりましたが、ある場面では「名前の辞書順」に並べたいかもしれません。

その場合は、Collections.sort(List<T> list, Comparator<? super T> c) を使います。

import java.util.*;

public class SortUserComparator {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        users.add(new User("Alice", 30));
        users.add(new User("Bob",   20));
        users.add(new User("Carol", 25));

        Comparator<User> byName = new Comparator<User>() {
            @Override
            public int compare(User u1, User u2) {
                return u1.name.compareTo(u2.name);
            }
        };

        Collections.sort(users, byName);

        System.out.println(users); // [Alice(30), Bob(20), Carol(25)]
    }
}
Java

Collections.sort の第 2 引数に「並べ方のルール(Comparator)」を渡すことで、
自然順序とは別のソートを実現できます。

Java 8 以降の書き方:Comparator.comparing

Java 8 以降はラムダ式と Comparator.comparing を使うと、さらに読みやすくなります。

Collections.sort(
        users,
        Comparator.comparing(u -> u.name)
);
Java

あるいは、List 側の sort メソッドを使っても同様です。

users.sort(Comparator.comparing(u -> u.name));
Java

複合条件や降順も簡単に書けます。

年齢昇順 → 同じ年齢なら名前昇順:

users.sort(
    Comparator.comparing((User u) -> u.age)
              .thenComparing(u -> u.name)
);
Java

年齢降順:

users.sort(
    Comparator.comparing((User u) -> u.age)
              .reversed()
);
Java

Collections.sort(list, comparator)list.sort(comparator) は本質的には同じですが、
新しいコードでは list.sort(...) を直接使うことが増えています。


Arrays.sort との違いと使い分け

配列なら Arrays.sort、List なら Collections.sort / List.sort

配列用のソートは Arrays.sort
List 用のソートは Collections.sort(+ List.sort)と覚えておくと整理しやすいです。

配列:

int[] a = {3, 1, 2};
Arrays.sort(a);  // a が [1, 2, 3] になる
Java

List:

List<Integer> list = Arrays.asList(3, 1, 2);
Collections.sort(list);  // list が [1, 2, 3] になる
Java

内部のアルゴリズム(Dual-Pivot QuickSort、TimSort など)は違いますが、
初心者のうちは

配列 → Arrays.sort
List → Collections.sort または list.sort

という対応だけ覚えておけば十分です。


「破壊的ソート」であることの注意点

元の順番は戻せない(戻したければコピーする)

Collections.sort(list) は、渡した list をその場で書き換えます。
元の順序を残しておきたい場合は、先にコピーを作っておく必要があります。

List<Integer> original = new ArrayList<>();
original.add(30);
original.add(10);
original.add(20);

List<Integer> sorted = new ArrayList<>(original);
Collections.sort(sorted);

System.out.println("元: " + original); // [30, 10, 20]
System.out.println("ソート後: " + sorted); // [10, 20, 30]
Java

new ArrayList<>(original) で「中身をコピーした別の List」を作り、
それに対して sort をかける、というパターンはよく使います。

ソート対象の List は「変更可能」である必要がある

Collections.sort は List の要素を入れ替えるので、
要素入れ替えをサポートしていない List に対しては UnsupportedOperationException が起こることがあります。

例えば、

List<Integer> list = Arrays.asList(3, 1, 2);
Collections.sort(list); // これは OK(中身の要素の入れ替えは許される実装)
Java

この例は動きますが、「読み取り専用ビュー」や特別な List 実装に対しては注意が必要です。

特に「Collections.unmodifiableList(...) でラップされた List」などは、
sort しようとすると例外になります。

「ソートしたい List は、本当に変更可能か?」は、頭の片隅で意識しておくとよいです。


まとめ:Collections.sort を自分の中でどう位置づけるか

Collections.sort を初心者向けに整理すると、こうなります。

  • List の中身を、その場で並び替えるメソッド(破壊的ソート)。
  • Collections.sort(list) が使えるのは、「要素が Comparable(自然順序を持つ)」とき。
  • 自作クラスは、自然順序で並べたいなら Comparable を実装する。
  • 自然順序とは別の並びにしたいときは、Collections.sort(list, comparator)Comparator を渡す。
  • 配列なら Arrays.sort、List なら Collections.sort / list.sort を使い分ける。

タイトルとURLをコピーしました