java.svg

【Java】Listのソート方法

Java

Collections クラス

Collectionsクラスは、ListMapに関連するstaticなメソッドが定義されています。 その中の 1 つにsort()があります。

sort()はオーバーロードされていて、Comparableを使うものとComparatorを使うものの 2 種類があります。

Comparable によるソート

以下はIntegerStringのソート方法の例になります。

Integer[] nums = {10, 4, 6, 2, 8};
List<Integer> list = Arrays.asList(nums);

//昇順ソート 2, 4, 6, 8, 10
Collections.sort(list);
//降順ソート 10, 8, 6, 4, 2
Collections.sort(list, Collections.reverseOrder());
String[] strs = {"ABC", "abc", "123", "1A", "A12"};
List<String> list = Arrays.asList(strs);

//昇順ソート 123, 1A, A12, ABC, abc
Collections.sort(list);
//降順ソート abc, ABC, A12, 1A, 123
Collections.sort(list, Collections.reverseOrder());

どちらもsort()の引数にListを渡すだけでソートされていることがわかります。

では、自作したクラスではどうでしょうか。

public class MyClass {
  private int num;
  private String str;

  //Constructor
  public MyClass(int num, String str) {
    this.num = num;
    this.str = str;
  }
}
List<MyClass> list = new ArrayList<>();
list.add(new MyClass(1, "ABC"));
list.add(new MyClass(2, "ABC"));
list.add(new MyClass(1, "BAC"));
list.add(new MyClass(0, "CCC"));
list.add(new MyClass(2, "000"));

//昇順ソート
Collections.sort(list);
//降順ソート
Collections.sort(list, Collections.reverseOrder());

これだけではソートは実行されません。なぜならどのようにソートをすれば良いかがわからないからです。 これを定義するのがComparableになります。

public class MyClass implements Comparable<MyClass> {
  //省略
  @Override
  public int compareTo(MyClass another) {
    int cmp = 0;
    //numを比較する
    if ((cmp = this.num - another.num) != 0) {
      return cmp;
    }
    //numの値が同じ場合はstrを比較する
    return this.str.compareTo(another.str);
  }
}

自作クラスをソートしたい場合は、インターフェースであるCompareblecompareTo()を実装します。

compareTo()は、自身と引数を比較した時に、大きければ正の値、小さければ負の値、同じであれば 0 になるように実装します。 これによってsort()は、並び替えの条件を判断しています。

上の例の昇順ソートは下記のようになります。

{ num: 0, str: "CCC" },
{ num: 1, str: "ABC" },
{ num: 1, str: "BAC" },
{ num: 2, str: "000" },
{ num: 2, str: "ABC" },

IntegerStringにはあらかじめComparableが実装されているためソートが実行されていました。

Comparator によるソート

Comparatorによるソートは、Comparableで定義したものとは違うルールでソートしたい場合に使用します。

例えば、先程のMyClassのソートをnum, strの順ではなく、str, numの順でソートしたいとします。

List<MyClass> list = new ArrayList<>();
list.add(new MyClass(1, "ABC"));
list.add(new MyClass(2, "ABC"));
list.add(new MyClass(1, "BAC"));
list.add(new MyClass(0, "CCC"));
list.add(new MyClass(2, "000"));

//ソートの定義
Comparator comparator = new Comparator<MyClass>() {
  @Override
  public int compare(MyClass a, MyClass b) {
    int cmp = 0;
    if ((cmp = a.str.compareTo(b.str)) != 0) {
      return cmp;
    }
    return a.num - b.num;
  }
}

//昇順ソート
Collections.sort(list, comparator);
//降順ソート
Collections.sort(list, comparator.reversed());

例のように無名クラスとしてComparatorcompare()を実装します。 compare()の実装は、ComparablecompareTo()を同じ考え方です。

これによって昇順ソートは下記のようになります。

{ num: 2, str: "000" },
{ num: 1, str: "ABC" },
{ num: 2, str: "ABC" },
{ num: 1, str: "BAC" },
{ num: 0, str: "CCC" },