[Java]StreamのgroupingByで、元の順序を保つ方法
groupingByを普通に書くと
例えば、あるString
のリストがあり、それぞれの個数を数えるケースを考えます。
普通に書くと以下のようになるかと思います。
Map<String, Long> collected = List.of("d", "c", "b", "a", "d", "c", "b", "d", "c", "d") .stream() .collect(Collectors.groupingBy( Function.identity(), Collectors.counting() )); System.out.println(collected); > Task :run {a=1, b=2, c=3, d=4}
この場合、groupingBy
の結果は、List
の順序は保証されません。
これは、戻り値として使用されるMap
自体が、順序が保証されていないためです。
LinkedHashMapを使う
そこで、引数を3つ取るgroupingByを使用します
public static <T, K, D, A, M extends Map<K, D>> Collector<T,?,M> groupingBy( Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream )
第2引数に順序が保証されるLinkedHashMap
のSupplier
を渡すことで、順序が保たれます。
Map<String, Long> collected = List.of("d", "c", "b", "a", "d", "c", "b", "d", "c", "d") .stream() .collect(Collectors.groupingBy( Function.identity(), LinkedHashMap::new, Collectors.counting() )); System.out.println(collected); > Task :run {d=4, c=3, b=2, a=1}
2引数のgroupingByの実装
参考までに、2引数のgroupingBy
の実装を見ると、内部では3引数のgroupingBy
に、順序の保たれないHashMap
を渡していることがわかります。
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) { return groupingBy(classifier, HashMap::new, downstream); }
参考
Collectors (Java SE 17 & JDK 17)
java - Stream doesn't preserve the order after grouping - Stack Overflow