Comparatorを用いたリストのソート方法について
javaのComparatorインタフェースを用いた、javaリストのソート方法についていくつか解説していきます。
私の経験ですが、「java リスト ソート」のように検索すると色々出てきますが、ソートを自前で用意するやり方やJavaで用意されているCollectionsを使ったやり方がいろいろ出てきて訳が分からなくなった経験があります。
「とりあえずjavaでソートする実例を知りたい」「そんでもってある程度自由にソートする方法を知りたい」方向けの記事です。
「そもそもComparatorってなに?」「Collectionsでのソートと何が違うの?」というところは私も把握できておらず、細かい部分は解説できないのですがざっと実用例を挙げていきたいと思います。本当はそこまで書きたいのですが理解力が足らず書けない…
Comparatorの公式リファレンスは以下。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Comparator.html
シンプルに昇順、降順してみる
実際にソートが必要となるシーンでは、より複雑かつ柔軟なソート条件になるとは思いますが、何事もまずは基本が大事。
- 昇順
ソートしたいListのsortメソッドの引数にComparator.naturalOrder()を渡せば昇順ソートできます。
list.sort(Comparator.naturalOrder());
実行
List<String> list = new ArrayList<>(Arrays.asList("abcde","bcdef","cdefg","bccdef"));
System.out.println("---before---");
for(String s : list) {
System.out.println(s);
}
System.out.println();
System.out.println("---after---");
list.sort(Comparator.naturalOrder());
for(String s : list) {
System.out.println(s);
}
結果
---before---
abcde
bcdef
cdefg
bccdef
---after---
abcde
bccdef
bcdef
cdefg
- 降順
ソートしたいListのsortメソッドの引数にComparator.reverseOrder()を渡せば降順ソートできます。
list.sort(Comparator.reverseOrder());
実行
List<String> list = new ArrayList<>(Arrays.asList("abcde","bcdef","cdefg","bccdef"));
System.out.println("---before---");
for(String s : list) {
System.out.println(s);
}
System.out.println();
System.out.println("---after---");
list.sort(Comparator.reverseOrder());
for(String s : list) {
System.out.println(s);
}
}
結果
---before---
abcde
bcdef
cdefg
bccdef
---after---
cdefg
bcdef
bccdef
abcde
もちろんですが昇順降順共に、文字列だけでなく数値もソートできます。
実行(あえて載せるほどでもないため昇順は割愛)
List<Integer> list = new ArrayList<>(Arrays.asList(111,222,333,100));
System.out.println("---before---");
for(int s : list) {
System.out.println(s);
}
System.out.println();
System.out.println("---after---");
list.sort(Comparator.reverseOrder());
for(int s : list) {
System.out.println(s);
}
結果
---before---
111
222
333
100
---after---
333
222
111
100
ソート条件を自分で定義してソートしてみる
実務でソートが求められるシーンでは、その要望は多岐にわたることが想定されるためシンプルな昇順降順では対応できない場面がほとんどです。
そのような場合に、ソート条件を自由に定義してソートする方法を記載していきます。
- 自前のメソッドの戻り値を利用し、ソートする
以下のような条件でソートします。今回は数値を対象。
・7がより多く含まれている順
ソート対象のlistのsortメソッドを実行するところまでは、前述の方法と同じ。
今回はsortの引数にComparator.comparingを渡します。
list.sort(Comparator.comparing(${クラス名}::${メソッド名},${昇順or降順})));
説明すると長くなりそうなのでとりあえず実例を載せます。
実行
List<Integer> list = new ArrayList<>(Arrays.asList(123,375,678,177,777,999));
System.out.println("---before---");
for(int s : list) {
System.out.println(s);
}
System.out.println();
System.out.println("---after---");
// getCountの戻り値を降順でソート
list.sort(Comparator.comparing(Qiita_Comparator::getCount,Comparator.reverseOrder()));
for(int s : list) {
System.out.println(s);
}
/**
* 7が含まれている数をカウント
* @param x 対象の数値
* @return 7が含まれている数
*/
static int getCount(int x) {
int count = 0;
String[] tmpbar = String.valueOf(x).split("");
for (String s : tmpbar) {
if (s.equals("7")) {
count += 1;
}
}
return count;
}
結果
7が含まれている順に、降順(reverseOrder)となっていることを確認。
---before---
123
375
678
177
777
999
---after---
777
177
375
678
123
999
解説
やっていることはかなりシンプルです。
listの要素をgetCountに渡し、その結果の戻り値が降順になるようにソートしています。
上記のやり方を応用すれば、ある程度自由に自分で条件を作成(上記でいうgetCountメソッドを変更)してソートできるかなと思います。
ソート条件を複数定義してソートしてみる
自前で条件を作成できたのはよしとして、次は複数の条件を設定してみましょう。
- 複数条件でソートする
以下のような条件でソートします。今回も数値を対象。
・7がより多く含まれている順
・7が含まれている数が同じの場合、より数値が大きい順
sortメソッドの引数にComparator.comparingと昇順or降順を渡すところまでは先ほどのと同じ。
今回はcomparingのthenComparingを呼び出し、ソート条件を追加します。
list.sort(Comparator.comparing(${クラス名}::${メソッド名},${昇順or降順}).thenComparing(m -> m,${昇順or降順}));
実行
List<Integer> list = new ArrayList<>(Arrays.asList(123,375,678,177,777,999));
System.out.println("---before---");
for(int s : list) {
System.out.println(s);
}
System.out.println();
System.out.println("---after---");
// getCountの戻り値を降順でソートし、戻り値が同じであればより数値が大きい順(降順)でソート
list.sort(Comparator.comparing(Qiita_Comparator::getCount,Comparator.reverseOrder()).thenComparing(m -> m,Comparator.reverseOrder()));
for(int s : list) {
System.out.println(s);
}
//getCountは変更なし
結果
7が含まれている順に降順かつ、含まれている数が同じならより数値が大きい順(reverseOrder)でソートされていることを確認。
---before---
123
375
678
177
777
999
---after---
777
177
678
375
999
123