LoginSignup
23
22

More than 5 years have passed since last update.

Streamでソートした結果をSetやMapで取得したいとき

Last updated at Posted at 2014-12-09

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
23
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
22