独自クラスが要素に入ったListのソートを行う
このページでは、List型のソート方法について記載しています。
特にListの中身が独自クラスの場合について記載しています。
独自クラスが要素に入ったListのソートを行う
シンプルなListのソート
まずは普通にListをソートする方法について確認したいと思います。
以下のコードは、Listの中身がString型の場合にソートを行う例です。
List<String> list = Arrays.asList("banana", "apple", "orange");
// 昇順ソート
list.sort(Comparator.naturalOrder());
// ソート結果
// apple
// banana
// orange
// 降順ソート
list.sort(Comparator.reverseOrder());
// ソート結果
// orange
// banana
// apple
}
このように、Listの中身がString型の場合は、Comparator.naturalOrder()を使って昇順ソート、
Comparator.reverseOrder()を使って降順ソートを行うことができます。
ちなみに、Collections.sort()を使ってソートする方法もあります。
以下のコードは、Collectionsを使ってListをソートする例です。
List<String> list = Arrays.asList("banana", "apple", "orange");
// 昇順ソート
Collections.sort(list);
// 降順ソート
Collections.sort(list, Comparator.reverseOrder());
ソート結果はこちらも同じ結果が出力されます。
Collection.sort()の方はjava 1.2から存在しており、List.sort()はjava 8から追加されたメソッドです。
どちらを使っても問題ありませんが、List.sort()の方がCollectionsを呼び出さなくて良く、
新しいメソッドのため、List.sort()を使ってこの記事では使用しています。
独自クラスが要素に入ったListのソートを行う
ここからは、独自クラスを含むListのソート方法について説明します。
独自クラスを含むListをソートする場合は、先ほど使用したComparator型のラムダ式を実装する必要があります。
以下にコードの例を記載します。
// 独自クラス
class Fruit {
String name;
int price;
String getName() {
return name;
}
int getPrice() {
return price;
}
Fruit(String name, int price) {
this.name = name;
this.price = price;
}
}
List<Fruit> list = Arrays.asList(new Fruit("banana", 100), new Fruit("apple", 150), new Fruit("orange", 120));
// Fruitクラスのnameで昇順ソート Comparator.comparing()でnameを比較してソートされます
Comparator<Fruit> comparator = Comparator.comparing(Fruit::getName);
// Comparator<Fruit> comparator = Comparator.comparing(x -> x.getName()); のラムダ式でもOK
list.sort(comparator);
// ソート結果
// name:apple, price:150
// name:banana, price:100
// name:orange, price:120
// Fruitクラスのnameで降順ソート 降順にする場合はreversed()を使います
Comparator<Fruit> reverseComparator = Comparator.comparing(Fruit::getName).reversed();
list.sort(reverseComparator);
// ソート結果
// name:orange, price:120
// name:banana, price:100
// name:apple, price:150
// Fruitクラスのpriceで昇順ソート
Comparator<Fruit> priceComparator = Comparator.comparing(Fruit::getPrice);
list.sort(priceComparator);
// ソート結果
// name:banana, price:100
// name:orange, price:120
// name:apple, price:150
コード例では、Fruitクラスを作成し、nameとpriceを持つクラスを作成しています。
その後、Comparator.comparing()に比較対象となるフィールド(getName()やgetPrice())を指定して、
list.sort()でソートを行っています。
Comparator型のラムダ式の実装をカスタマイズすることで複雑なソートも可能です。
例えば、複数の要素でソートしたい場合はComparator.comparing()の後にthenComparing()を使って比較することができます。
nameが同じ場合はpriceの降順で比較することができます。
List<Fruit> list =
Arrays.asList(new Fruit("banana", 100), new Fruit("apple", 150), new Fruit("orange", 120), new Fruit("apple", 300));
// nameで昇順ソート、nameが同じ場合はpriceの降順ソート
Comparator<Fruit> customComparator = Comparator.comparing(Fruit::getName)
.thenComparing(Fruit::getPrice, Comparator.reverseOrder());
list.sort(customComparator);
// ソート結果
// name:apple, price:300
// name:apple, price:150
// name:banana, price:100
// name:orange, price:120
このように複数のフィールドを比較することができます。
昇順、降順を指定する際に1点、注意点があります。
昇順、降順は以下のようにも指定できます。
// nameで昇順ソート、nameが同じ場合はpriceの降順ソート
Comparator<Fruit> customComparator = Comparator.comparing(Fruit::getName)
.thenComparing(Fruit::getPrice)
.reversed();
list.sort(customComparator);
// ソート結果
// name:orange, price:120
// name:banana, price:100
// name:apple, price:300
// name:apple, price:150
このように、Comparator.comparing()の後にreversed()を使って降順にすることもできます。
ただし、thenComparing()の後にreversed()を使うと、そこまでに記載されている要素のソートも
逆順になってしまうので、注意が必要です。
昇順、降順が混在する場合は、1つ目のコードのような書き方で指定するのが良いかと思います。
リストの要素にNullが含まれる場合
リストにNullが含まれる場合、ソートを行うとNullPointerExceptionが発生します。
そのため、Nullを含むリストをソートする際は、Nullを扱うためのComparatorを使用することが重要です。
以下にその方法を示します。
List<Fruit> list = Arrays.asList(new Fruit("banana", 100), new Fruit("apple", 150), new Fruit("orange", 120) ,new Fruit(null, 500));
Comparator<Fruit> customComparator = Comparator.comparing(Fruit::getName, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(Fruit::getPrice);
list.sort(customComparator);
上記のコードでは、Comparator.nullsLast()を使用して、Nullをリストの最後に移動させています。
このようにして、Nullを含むリストでも安全にソートを行うことができます。
さらに、Comparator.nullsFirst()を使用することで、Nullをリストの最初に移動させることも可能です。