Collectors.toSet()は順序が保証されない
StreamAPIで結果をSetで取得したいときはCollectorsのtoSet()を使用するが、toSet()は結果の順序が保証されない。
ソースを見ると、toSet()はHashSetのインスタンスを生成している。
Collectors.java
public static <T>
Collector<T, ?, Set<T>> toSet() {
return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
(left, right) -> { left.addAll(right); return left; },
CH_UNORDERED_ID);
}
その為、Streamのsorted()でソートしても結果を取得する時にtoSet()を使うと順序がおかしくなってしまう。
toSet()では結果の順序がおかしくなる
Set<Integer> set = Stream.of(30, 20, 50, 10, 40)
.sorted()
.peek(x -> System.out.print(x + " ")) // => 10 20 30 40 50
.collect(Collectors.toSet());
System.out.println(set); // => [50, 20, 40, 10, 30]
ソート結果をSetで取得する
ソート結果をSetで取得したい時は、collect()Collectors.toCollection()
でLinkedHashSetを指定すれば良い。
ソート結果をLinkedHashSetで取得
Set<Integer> set = Stream.of(30, 20, 50, 10, 40)
.sorted()
//.collect(LinkedHashSet::new, Set::add, Set::addAll);
.collect(Collectors.toCollection(LinkedHashSet::new));
System.out.println(set); // => [10, 20, 30, 40, 50]
あるいは、Stream.sorted()
を使用せずに、collect()
でCollectors.toCollection()
でTreeSet(SortedSetの実装クラス)を指定する。
TreeSetで結果を取得
Set<Integer> set = Stream.of(30, 20, 50, 10, 40)
//.collect(TreeSet::new, Set::add, Set::addAll);
.collect(Collectors.toCollection(TreeSet::new));
System.out.println(set); // => [10, 20, 30, 40, 50]
ソート結果をMapで取得する
Mapの場合も引数が少ないほうのtoMap()では順序が保証されない。
toMap()でソート結果がおかしくなる
Map<Integer, String> map = Stream.of(30, 20, 50, 10, 40)
.sorted()
.peek(x -> System.out.print(x + " ")) // => 10 20 30 40 50
.collect(Collectors.toMap(Function.identity(), String::valueOf));
System.out.println(map); // => {50=50, 20=20, 40=40, 10=10, 30=30}
ソート結果をMapで取得するには、引数でmapSupplierを指定するtoMap()を使用してLinkedHashMapかTreeMapを指定すれば良い。
toMap()でLinkedHashMapを指定する
Map<Integer, String> map = Stream.of(30, 20, 50, 10, 40)
.sorted()
.collect(Collectors.toMap(
Function.identity(),
String::valueOf,
(u, v) -> v,
LinkedHashMap::new
));
System.out.println(map); // => {10=10, 20=20, 30=30, 40=40, 50=50
toMap()でTreeMapを指定する
Map<Integer, String> map = Stream.of(30, 20, 50, 10, 40)
.collect(Collectors.toMap(
Function.identity(),
String::valueOf,
(u, v) -> v,
TreeMap::new
));
System.out.println(map); // => {10=10, 20=20, 30=30, 40=40, 50=50