IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

Javaのstreamで嵌る

IntStreamStream<T>インターフェースに親子関係がない

 java.lang.AutoCloseable
     java.util.stream.BaseStream<T,S>
         java.util.stream.DoubleStream
         java.util.stream.IntStream
         java.util.stream.LongStream
         java.util.stream.Stream<T>

そのために(?)、IntSteramに3つの引数をもつreduceがない。これに気がつかず、相当時間を無駄にした。

順次および並列の集約操作をサポートするプリミティブint値要素のシーケンスです。これは、Streamに対してintプリミティブ特殊化を行ったものです。

とドキュメントにはあるが、特殊化といっても型だけ特殊化されているのではない。

多分、このようなクラス階層になっているのは、IntStreamIntegerではなくintだからなんだろうけど。

Streamインターフェースの2つの引数を持つreduceと3つの引数を持つreduceで2番目のパラメータ部分の型が異なる。

2引数

reduce(T identity, BinaryOperator accumulator)

3引数

reduce(U identity, BiFunction; < U,? super T,U > accumulator, BinaryOperator < U > combiner)

2引数の場合、BinaryOperatorの型がTなので、accumlatorの型はStreamの型と同じ必要がある。

また、実際のコーディングでは、3引数のcombinerのコーディングが定型的になる(と思うので)冗長になる。

intelliJ型推論が微妙にずれている

3引数reduceでパラメータの3番目が間違っているのに、2番目のエラーがでる。javacでは3番目のパラメータのコンパイルエラーになる。

f:id:naotoogawa:20181202152735p:plain
第3パラメータの問題なのに第2パラメータのエラーと表示される

f:id:naotoogawa:20181202152730p:plain
コンパイルした時のエラー

単位元?

T reduce(T identity, BinaryOperator accumulator)

指定された単位元の値と結合的な累積関数を使ってこのストリームの要素に対してリダクションを実行し、リデュースされた値を返します。これは、次の操作に相当します。

足し算の単位元は0だけど、0でなくても動作する。

        int r = Stream.of(1,2,3).reduce(10, (x,y) -> x + y);
        System.out.println(r); // 16

サンプル

Snake caseCamel caseの返還をstreamやラムダで書くといい感じになるかなと思ったのですが、そうでもないですね。

    private static String toCamel(String str) {
        return Arrays.stream(str.split("_"))
            .map(x -> x.substring(0,1).toUpperCase() + x.substring(1).toLowerCase())
            .collect(Collectors.joining());
    }

    private static String toSnake(String in) {
        String out = in.codePoints()
            .mapToObj(c -> (char)c) // IntStream to Stream<Character>
            .reduce("" ,
                (acc, v) -> acc + (Character.isLowerCase(v) ? v : "_" + Character.toLowerCase(v)) ,
                (s1, s2) -> s1 + s2
            );
        return out;
    }

もっと簡潔にかけるのなら教えて下さい。

感想

Javastreamは、慣れてないだけかもしれないが、難しい。というか面倒。

reduceは可読性が上がらなさそう。結局2変数の関数を作らないといけないこともあるので。

stream処理を考えた時には、reduceを使わない範囲で使うと良いのではないか。

reduce使うくらいならforでループして処理するのが良さそう。