Comparatorの使い方・作り方
Java SE 8のdefaultメソッドの導入や関数合成などを取り入れ大幅に刷新された。
Java | Java SE 8 |
概要
コレクションフレームワークの部品。Comparatorを実装することでComparableではないオブジェクトもソートできるようになる。
Comparatorでabstractなのはcompare()メソッドしかないのでこれを実装する。オブジェクトの大小比較し結果をint型の戻り値によって返す。
-
o1 < o2 なら -1
-
o1 == o2 なら 0
-
o1 > o2 なら -1
Java SE 8より前であればcompare()を頑張って実装するしかなかったが、defaultメソッドの導入や関数合成などを取り入れ大幅に刷新された。関数合成で組み合わせることで、compare()を書くことがほとんどいらない。
使い方
Java SE 8より前はCollectionsによるソートが主だったが、利用できるクラスやメソッドも増えた。
クラス・メソッド | 概要 |
---|---|
Steram#sorted() |
Java SE 8から。ソートの結果を別のコレクションで返すことになる。 |
List#sorted() |
Java SE 8から。なぜかなかった。 |
Collections.sort() |
Java SE 8より前ならこれ。 |
Arrays.sort() |
配列をソートする。 |
Streamで並べ替えると元のコレクションとは別のコレクションを生成することになるので有利。あちこちでソート結果を共有してしまうと別のソートをしたくなったときに困るので、ソート結果はソート前のコレクションとは別のコレクションにしておくのがベター。
List<Integer> numbers = Arrays.asList(3, 2, 4, 1);
//Streamによるソート
List<Integer> sorted = numbers.stream()
.sorted(Comparator.naturalOrder())
.collect(Collectors.toList());
//Collectionsによるソート
Collections.sort(numbers,Comparator.naturalOrder());
//List#sort()によるソート
numbers.sort(Comparator.naturalOrder());
//Arraysによるソート
Integer[] numberArray = new Integer[] { 3, 2, 4, 1 };
Arrays.sort(numberArray, Comparator.naturalOrder());
作り方
compare()を実装する以外にも、defaultメソッドや関数合成などが利用できる。compare()を直接書くことがいらなくなってきた。
ComparableをソートするComparatorを作る
Comparableなものは多いので使うことが多い。ただ自然順序順であればComparatorを指定しなくてよい場合もある。
メソッド | 概要 |
---|---|
naturalOrder() |
自然な順序。 |
reverseOrder() |
自然順序付けの逆。 |
List<Integer> numbers = Arrays.asList(3, 2, 4, 1);
List<Integer> naturalSorted = numbers.stream()
.sorted(Comparator.naturalOrder())
.collect(Collectors.toList());
List<Integer> reverseSorted = numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println("numbers:" + numbers);
System.out.println("naturalSorted:" + naturalSorted);
System.out.println("reverseSorted:" + reverseSorted);
numbers:[3, 2, 4, 1]
naturalSorted:[1, 2, 3, 4]
reverseSorted:[4, 3, 2, 1]
ComparableでないオブジェクトのComparatorを作る
これまでであればcompareTo()がマストだったが、オブジェクト内のComparableを利用して簡単に作れるメソッドが追加された。
comparing()はこの他にプリミティブ型に対応している「comparingInt()」「comparingLong()」「comparingDouble()」などもある。
メソッド | 概要 |
---|---|
comparing(Function<? super T,? extends U> keyExtractor) |
keyExtractorで指定された値で比較するComparator。 |
List<Person> persons = new ArrayList<>();
persons.add(new Person("taro", 25));
persons.add(new Person("jiro", 22));
persons.add(new Person("saburo", 23));
Comparator<Person> nameComparator = Comparator.comparing(Person::getName);
List<Person> sortedPersons = persons.stream()
.sorted(nameComparator)
.collect(Collectors.toList());
System.out.println(" original:" + persons);
System.out.println("name sorted:" + sortedPersons);
original:[Person(name=taro, age=25), Person(name=jiro, age=22), Person(name=saburo, age=23)]
name sorted:[Person(name=jiro, age=22), Person(name=saburo, age=23), Person(name=taro, age=25)]
Comparatorを組み合わせてComparatorを作る
SQLであれば「ORDER BY colA,colB」みたいなことを、いままではcompareTo()を頑張るしかなかったが、これも簡単にできるメソッドが追加された。
いわゆる関数合成。いくらでも組み合わせることができる。この方法を用いればホントに特殊なモノ以外はcompareTo()を直接書かずにComparatorを作れる。
メソッド | 概要 |
---|---|
reversed() |
自然な順序。 |
thenComparing(Comparator<? super T> other) |
compareTo()==0の場合にotherが適用されるComparator |
List<Person> persons = new ArrayList<>();
persons.add(new Person("taro", 25));
persons.add(new Person("jiro", 22));
persons.add(new Person("saburo", 23));
persons.add(new Person("shiro", 25));
Comparator<Person> personComparator =
Comparator.comparing(Person::getAge)
.reversed()
.thenComparing(Comparator.comparing(Person::getName));
List<Person> sortedPersons = persons.stream()
.sorted(personComparator)
.collect(Collectors.toList());
System.out.println("original:" + persons);
System.out.println(" sorted:" + sortedPersons);
original:[Person(name=taro, age=25), Person(name=jiro, age=22), Person(name=saburo, age=23), Person(name=shiro, age=25)]
sorted:[Person(name=shiro, age=25), Person(name=taro, age=25), Person(name=saburo, age=23), Person(name=jiro, age=22)]
Appendix A: 参考
Appendix B: 改訂履歴
-
v1.0,2016-07-24: 初稿