23

【Java】IntStreamで要素を逆順にソートする

はじめに

こちらの記事にも書いている、IntStream.range()とIntStream.rangeClosed()は、指定した範囲の連続する値を得る際に非常に便利です。

しかし、これらのメソッドで得られたIntStreamは、sortedメソッドだけでは逆順にできません。

例えば以下のコードの場合、sortedメソッドの引数にComparator.reverseOrder()をとることが出来ないため、コンパイルエラーとなってしまいます。
IntStreamクラスのsortedメソッドは、引数を持たないメソッドとして定義されています。

    static void reverse() {
        IntStream.rangeClosed(1, 10)                        // IntStreamの生成
            .sorted(Comparator.reverseOrder())              // 中間操作
            .forEach(num -> System.out.println(num));       // 終端処理
    }

解決策1:boxedメソッドの利用

中間処理としてboxedメソッドを入れて、IntStreamをStream<Integer>に変換することで、sortedメソッドだけで簡単に逆順にすることができました。

Streamクラスのsortedメソッドは、Comparatorを引数にとることができます。

    static void reverse() {
        IntStream.rangeClosed(1, 10)                        // IntStreamの生成
            .boxed()                                        // ★追加した中間操作(IntStream->Stream<Integer>)
            .sorted(Comparator.reverseOrder())              // 中間操作
            .forEach(num -> System.out.println(num));       // 終端処理
    }

解決策2:IntStreamの使用を諦める

IntStreamを使わずにStream(Stream<Integer>)を使えば、この問題を回避することができます。

その代わり、IntStream.range()とやIntStream.rangeClosed()などは使えなくなってしまいます...

    static void reverse2() {
        Integer[] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        Stream<Integer> stream = Arrays.stream(values);
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

        Arrays.stream(values)                                // streamの生成
            .sorted(Comparator.reverseOrder())               // 中間操作
            .forEach(num -> System.out.println(num));        // 終端処理
    }

    /**
     * @k73i55no5さんによる改善版。listを介さずに直接配列からstreamに変換している。
     */
    static void reverse3() {
        Integer[] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        Arrays.stream(values)                               // streamの生成
            .map(num -> num * 2)                            // 中間操作
            .sorted(Comparator.reverseOrder())              // 中間操作
            .forEach(num -> System.out.println(num));       // 終端処理
    }

解決策3:符号を変換してからソートする

@swordoneさんからコメントで教えて頂いた方法で、「プラスマイナスの符号を変換して並べ替えた後、再び符号を元に戻す」というものです。

こちらでは未確認ですが、この方法を使うには幾つかの制約があるようです。
コメントでは「ネタ」と言われていますが、全く想定していない方向からボールが飛んできたように感じるほど驚きました。

    static void reverse4() {
        IntStream.rangeClosed(1, 10)                         // streamの生成
            .map(i -> -i)                                    // 中間操作(正の数->負の数)
            .sorted()                                        // 中間操作
            .map(i -> -i)                                    // 中間操作(負の数->正の数)
            .forEach(num -> System.out.println(num));        // 終端処理
    }

まとめ

  • 「IntStreamをStream<Integer>に変換する」という方法で解決できるとは思いませんでした。
    • そもそも、IntStreamとStream<Integer>の違いを全く理解していなかったです...
  • 解決策2は単なる逃げですが、絶対にIntStreamを使わなければダメということでなければ、これで良いのかもしれません。
  • Java8のStream APIは苦手(というより使う機会がない)なので、少しでも触れる機会を増やしていきたいです。
    • @LouiS0616さんのコメント(※mapメソッドを使う方法)については、もう少し勉強してから追記したいと思います。
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away