こんにちは! ログログです。
34歳で開発未経験から開発エンジニアになり、今ではフリーランスエンジニアとして働いています!
JavaのStreamでソートをしたいんだけど、どうやるのかパッとコードが出てこないということはありませんか?
本記事ではStreamでソートする様々なケースを考慮してサンプルを掲載していますのでぜひ参考にして下さい!
ソートのケース
String値
Stringの場合、アルファベットを元にソートすることができます。
降順ソートにしたい場合はsortedの引数にComparator.reverseOrder()を与えます。(昇順/降順を参照)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import java.util.List; import static java.util.stream.Collectors.toList; public class StringSortExample { public static void main(String[] args) { List<String> strings = List.of("Banana", "Apple", "Cherry", "Date", "Elderberry"); List<String> sortedStrings = strings.stream() .sorted() .collect(toList()); sortedStrings.forEach(System.out::println); } } // Apple // Banana // Cherry // Date // Elderberry |
Int値
intの場合(実際にはIntegerで保持することになりますが)、数値としてソートされます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import java.util.List; import static java.util.stream.Collectors.toList; public class IntSortExample { public static void main(String[] args) { List<Integer> numbers = List.of(5, 2, 9, 1, 8, 3); List<Integer> sortedNumbers = numbers.stream() .sorted() .collect(toList()); sortedNumbers.forEach(System.out::println); } } // 1 // 2 // 3 // 5 // 8 // 9 |
昇順/降順
昇順では特に指定する必要はありませんが、明示的にComparator.naturalOrder()を与えることもできます。
降順の場合、Comparator.reverseOrder()をsortedメソッドの引数に与えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import java.util.Comparator; import java.util.List; import static java.util.stream.Collectors.toList; public class ReverseSortExample { public static void main(String[] args) { List<Integer> numbers = List.of(5, 2, 9, 1, 8, 3); List<Integer> ascending = numbers.stream() .sorted() // .sorted(Comparator.naturalOrder()) 明示的に指定も可能 .collect(toList()); List<Integer> descending = numbers.stream() .sorted(Comparator.reverseOrder()) .collect(toList()); System.out.println("Ascending:"); ascending.forEach(System.out::println); System.out.println("Descending:"); descending.forEach(System.out::println); } } // Ascending: // 1 // 2 // 3 // 5 // 8 // 9 // Descending: // 9 // 8 // 5 // 3 // 2 // 1 |
複数条件
複数条件を指定したい場合、thenComparingを追加していきます。
最初に指定した条件から優先的に適用されるので、以下の例では [ 年齢 > 名前 ] の順でソートされています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import java.util.Comparator; import java.util.List; import static java.util.stream.Collectors.toList; public class MultiFieldSortExample { public static void main(String[] args) { List<Person> people = List.of( new Person("Alice", 25), new Person("Bob", 30), new Person("Charlie", 22), new Person("David", 30) ); List<Person> sortedPeople = people.stream() .sorted(Comparator.comparing(Person::getAge) .thenComparing(Person::getName)) .collect(toList()); sortedPeople.forEach(person -> System.out.println(person.getName() + " - " + person.getAge())); } static class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } } // Charlie - 22 // Alice - 25 // Bob - 30 // David - 30 |
null値を含む
nullが含まれるキーをソートする場合、Comparator.nullsFirst または Comparator.nullsLast を使用する必要があります。
Comparator.nullsFirstはnullが最初になるようにソートし、Comparator.nullsLastは最後になるようにソートします。
使用しなかった場合は NullPinterException がスローされるので注意が必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import java.util.Arrays; import java.util.List; import java.util.Comparator; import static java.util.stream.Collectors.toList; public class NullSortExample { public static void main(String[] args) { List<String> strings = Arrays.asList("Banana", "Apple", "Cherry", null, "Date", "Elderberry"); List<String> sortedStrings = strings.stream() .sorted(Comparator.nullsFirst(Comparator.naturalOrder())) .collect(toList()); sortedStrings.forEach(System.out::println); } } // null // Apple // Banana // Cherry // Date // Elderberry |
ソート対象オブジェクトでComparableを実装する
ソート対象のクラスにComparableが実装してあれば、OverrideされたcompareToに従って比較が行われることでソート順が規定されます。
compareToの比較をソート順としたい場合、sortedメソッドに追加の条件は必要はありません。
以下の例では、scoreがcompareToメソッドで比較されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
import java.util.List; import static java.util.stream.Collectors.toList; public class ComparableSortExample { public static void main(String[] args) { List<Student> students = List.of( new Student("Alice", 90), new Student("Bob", 75), new Student("Charlie", 82) ); List<Student> sortedStudents = students.stream() .sorted() .collect(toList()); sortedStudents.forEach( student -> System.out.println(student.getName() + " - " + student.getScore())); } /** * Studentクラス * Comparableを実装し、compareToをOverrideすることで比較可能にしている。 * これによりソート順が規定される。 */ static class Student implements Comparable<Student> { private String name; private int score; public Student(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public int getScore() { return score; } @Override public int compareTo(Student other) { return Integer.compare(this.score, other.score); } } } // Bob - 75 // Charlie - 82 // Alice - 90 |
Comparatorを指定する
Comparatorを使用することで対象クラスのフィールドでソートすることができます。
以下の例ではscoreの降順でソートしています。
ちなみに、対象クラスにComparableが実装されている場合でも、Comparatorの指定が優先されるので注意してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import java.util.List; import java.util.Comparator; import static java.util.stream.Collectors.toList; public class ComparatorSortExample { public static void main(String[] args) { List<Student> students = List.of( new Student("Alice", 90), new Student("Bob", 75), new Student("Charlie", 82) ); List<Student> sortedStudents = students.stream() .sorted(Comparator.comparing(Student::getScore).reversed()) .collect(toList()); sortedStudents.forEach( student -> System.out.println(student.getName() + " - " + student.getScore())); } static class Student implements Comparable<Student> { private String name; private int score; public Student(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public int getScore() { return score; } @Override public int compareTo(Student other) { return Integer.compare(this.score, other.score); } } } // Alice - 90 // Charlie - 82 // Bob - 75 |
Mapをソートする
Mapのソートではkeyまたはvalueのいずれでソートするかを指定します。
以下の例ではvalueで昇順ソートしていますが、Map.Entry.comparingByKey() を使用することでkeyによるソートが可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import java.util.*; import static java.util.stream.Collectors.toMap; public class MapSortExample { public static void main(String[] args) { Map<String, Integer> unsortedMap = new HashMap<>(); unsortedMap.put("Alice", 90); unsortedMap.put("Bob", 75); unsortedMap.put("Charlie", 82); Map<String, Integer> sortedMap = unsortedMap.entrySet().stream() .sorted(Map.Entry.comparingByValue()) .collect(toMap( Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); sortedMap.forEach( (key, value) -> System.out.println(key + " - " + value)); } } // Bob - 75 // Charlie - 82 // Alice - 90 |
まとめ
- Streamのソートではsortedという中間操作を行う。
- String、intにおいては単にsortedを指定するだけでよい。
- 昇順は特に指定不要だが、降順ではComparator.reverseOrder()を指定する。
- 複数条件の場合、thenComparingで条件を追加していく。
- nullを含む場合は、Comparator.nullsFirst または Comparator.nullsLastを使用する。
- ソート対象オブジェクトでComparableを実装されている場合、compareToによる比較が行われる。
- Comparatorを指定することでソート順を指定できる。その場合、Comparableの実装よりも優先される。
- Mapのソートではkeyかvalueのいずれでソートするかを指定する。
コメント