web-dev-qa-db-ja.com

2つのJava 8ストリーム、またはストリームへの追加要素の追加

このように、ストリームや追加の要素を追加することができます。

Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));

そして、このように新しいものを追加することができます。

Stream stream = Stream.concat(
                       Stream.concat(
                              stream1.filter(x -> x!=0), stream2)
                              .filter(x -> x!=1),
                                  Stream.of(element))
                                  .filter(x -> x!=2);

concatは静的なので、これは醜いです。 concatがインスタンスメソッドの場合、上記の例は読みやすくなります。

 Stream stream = stream1.concat(stream2).concat(element);

そして

 Stream stream = stream1
                 .filter(x -> x!=0)
                 .concat(stream2)
                 .filter(x -> x!=1)
                 .concat(element)
                 .filter(x -> x!=2);

私の質問は:

1)concatが静的である理由は何かありますか?それとも、私が欠けている同等のインスタンスメソッドがありますか?

2)いずれにせよ、これを行うためのより良い方法はありますか?

156
MarcG

Stream.concatおよびStream.of静的インポートを追加すると、最初の例は次のようになります。

Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));

一般的な名前で静的メソッドをインポートすると、コードの読み取りや管理が困難になる可能性があります(名前空間の汚染)。そのため、もっと意味のある名前を付けて独自の静的メソッドを作成した方が良いかもしれません。しかし、デモンストレーションのために私はこの名前を使い続けます。

public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
    return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
    return Stream.concat(lhs, Stream.of(rhs));
}

これら2つの静的メソッド(オプションで静的インポートと組み合わせて)を使用すると、2つの例は次のように書くことができます。

Stream<Foo> stream = concat(stream1, concat(stream2, element));

Stream<Foo> stream = concat(
                         concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
                         element)
                     .filter(x -> x!=2);

コードが大幅に短くなりました。しかし、読みやすさは向上していないことに同意します。だから私は別の解決策があります。


多くの場合、Collectorsはストリームの機能性をextendするために使用できます。下部に2つのCollectorsがあるので、2つの例は次のように書くことができます。

Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));

Stream<Foo> stream = stream1
                     .filter(x -> x!=0)
                     .collect(concat(stream2))
                     .filter(x -> x!=1)
                     .collect(concat(element))
                     .filter(x -> x!=2);

目的の構文と上記の構文の唯一の違いは、concat(...)collect(concat(...))に置き換える必要があることです。 2つの静的メソッドは次のように実装できます(オプションで静的インポートと組み合わせて使用​​)。

private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
    return Collector.of(
        collector.supplier(),
        collector.accumulator(),
        collector.combiner(),
        collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
    return combine(Collectors.toList(),
        list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
    return concat(Stream.of(element));
}

もちろん、このソリューションには言及すべき欠点があります。 collectは、ストリームのすべての要素を消費する最後の操作です。それに加えて、コレクターconcatは、チェーン内で使用されるたびに中間型ArrayListを作成します。どちらの操作もプログラムの動作に大きな影響を与える可能性があります。ただし、読みやすさパフォーマンスよりも重要である場合は、それでもまだ非常に便利な方法です。

123
nosid

残念ながら、この答えはおそらくほとんどまたはまったく役に立ちませんが、私はこの設計の原因を見つけることができるかどうかを確認するために、Java Lambdaメーリングリストのフォレンジック分析を行いました。これは私が見つけたものです。

初めはStream.concat(Stream)のインスタンスメソッドがありました

メーリングリストで、このメソッドが元々インスタンスメソッドとして実装されていたことがわかります。これは、Paul Sandozによる連結操作についての このスレッド を参照してください。

その中で彼らは、流れが無限になる可能性があるそれらのケースから生じる可能性がある問題とそれらのケースでどんな連結が意味するであろうかについて議論します、しかし私はそれが修正の理由ではないと思います。

あなたは この他のスレッド を見ると、JDK 8の初期のユーザの何人かは、null引数で使われた場合のconcatインスタンスメソッドの振る舞いについて疑問を投げかけました。

これは other thread ですが、concatメソッドの設計については議論中です。

Streams.concat(Stream、Stream)にリファクタリングされました

しかし、何の説明もなく、突然、メソッドが静的メソッドに変更されました。これは ストリームの結合に関するこのスレッド に示すとおりです。これはおそらく、この変更についてちょっとした光を当てる唯一のメールスレッドですが、私がリファクタリングの理由を判断するのに十分明確ではありませんでした。しかし、彼らは コミットしました 彼らはconcatメソッドをStreamからヘルパークラスStreamsへ移動することを提案しています。

Stream.concat(Stream、Stream)にリファクタリングされました

後に 再び動かされましたStreamsからStreamへ、しかしそれについての説明もありません。

だから、要するに、デザインの理由は私にとって完全には明らかではないし、私は良い説明を見つけることができませんでした。私はあなたがまだメーリングリストで質問をすることができると思います。

ストリーム連結のいくつかの選択肢

これ Michael Hixsonによる他のスレッド ストリームを結合するための他の方法について議論する/尋ねる

  1. 2つのストリームを結合するために、私はこれをするべきです:

    Stream.concat(s1, s2)
    

    これではない:

    Stream.of(s1, s2).flatMap(x -> x)
    

    ... 右?

  2. 3つ以上のストリームを結合するために、私はこれをするべきです:

    Stream.of(s1, s2, s3, ...).flatMap(x -> x)
    

    これではない:

    Stream.of(s1, s2, s3, ...).reduce(Stream.empty(), Stream::concat)
    

    ... 右?

158
Edwin Dalorzo

My StreamEx ライブラリはStream APIの機能を拡張します。特にこの問題を解決する appendprepend のようなメソッドを提供しています(内部的にはconcatを使います)。これらのメソッドは、別のストリーム、コレクション、または可変引数配列のいずれかを受け入れることができます。私のライブラリを使用することであなたの問題はこのように解決することができます(x != 0は非プリミティブストリームのために奇妙に見えることに注意してください):

Stream<Integer> stream = StreamEx.of(stream1)
             .filter(x -> !x.equals(0))
             .append(stream2)
             .filter(x -> !x.equals(1))
             .append(element)
             .filter(x -> !x.equals(2));

ちなみにfilter操作のショートカットもあります。

Stream<Integer> stream = StreamEx.of(stream1).without(0)
                                 .append(stream2).without(1)
                                 .append(element).without(2);
12
Tagir Valeev

ただしてください:

Stream.of(stream1, stream2, Stream.of(element)).flatMap(identity());

identity()Function.identity()の静的インポートです。

複数のストリームを1つのストリームに連結することは、ストリームをフラット化することと同じです。

ただし、残念ながら、何らかの理由でStreamflatten()メソッドがないため、flatMap()をidentity関数と共に使用する必要があります。

9
herman

Guavaの Streams.concat(Stream<? extends T>... streams) というメソッドを使うことができます。これは静的インポートでは非常に短くなります。

Stream stream = concat(stream1, stream2, of(element));
2
Kunda

結局のところ、私はストリームを結合することに興味はありませんが、それらすべてのストリームの各要素を処理した結果を結合することに関心があります。

ストリームを結合するのは面倒なことになるかもしれませんが(したがってこのスレッドではありますが)、それらの処理結果を結合するのはかなりのことです。

解決するための鍵はあなた自身のコレクターを作成し、新しいコレクターのための供給者関数が毎回同じコレクションを返すことを確実にすることです(新しいものではない)、以下のコードはこのアプローチを説明します。

package scratchpad;

import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.List;
import Java.util.stream.Collector;
import Java.util.stream.Stream;

public class CombineStreams {
    public CombineStreams() {
        super();
    }

    public static void main(String[] args) {
        List<String> resultList = new ArrayList<>();
        Collector<String, List<String>, List<String>> collector = Collector.of(
                () -> resultList,
                (list, item) -> {
                    list.add(item);
                },
                (llist, rlist) -> {
                    llist.addAll(rlist);
                    return llist;
                }
        );
        String searchString = "Wil";

        System.out.println("After processing first stream\n"
                + createFirstStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

        System.out.println("After processing second stream\n"
                + createSecondStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

        System.out.println("After processing third stream\n"
                + createThirdStream().filter(name -> name.contains(searchString)).collect(collector));
        System.out.println();

    }

    private static Stream<String> createFirstStream() {
        return Arrays.asList(
                "William Shakespeare",
                "Emily Dickinson",
                "H. P. Lovecraft",
                "Arthur Conan Doyle",
                "Leo Tolstoy",
                "Edgar Allan Poe",
                "Robert Ervin Howard",
                "Rabindranath Tagore",
                "Rudyard Kipling",
                "Seneca",
                "John Donne",
                "Sarah Williams",
                "Oscar Wilde",
                "Catullus",
                "Alfred Tennyson",
                "William Blake",
                "Charles Dickens",
                "John Keats",
                "Theodor Herzl"
        ).stream();
    }

    private static Stream<String> createSecondStream() {
        return Arrays.asList(
                "Percy Bysshe Shelley",
                "Ernest Hemingway",
                "Barack Obama",
                "Anton Chekhov",
                "Henry Wadsworth Longfellow",
                "Arthur Schopenhauer",
                "Jacob De Haas",
                "George Gordon Byron",
                "Jack London",
                "Robert Frost",
                "Abraham Lincoln",
                "O. Henry",
                "Ovid",
                "Robert Louis Stevenson",
                "John Masefield",
                "James Joyce",
                "Clark Ashton Smith",
                "Aristotle",
                "William Wordsworth",
                "Jane Austen"
        ).stream();
    }

    private static Stream<String> createThirdStream() {
        return Arrays.asList(
                "Niccolò Machiavelli",
                "Lewis Carroll",
                "Robert Burns",
                "Edgar Rice Burroughs",
                "Plato",
                "John Milton",
                "Ralph Waldo Emerson",
                "Margaret Thatcher",
                "Sylvie d'Avigdor",
                "Marcus Tullius Cicero",
                "Banjo Paterson",
                "Woodrow Wilson",
                "Walt Whitman",
                "Theodore Roosevelt",
                "Agatha Christie",
                "Ambrose Bierce",
                "Nikola Tesla",
                "Franz Kafka"
        ).stream();
    }
}
1
Legna

もしあなたがサードパーティのライブラリを使っても構わないのなら cyclops-react あなたはappend/prepend演算子を通してそれをすることを可能にする拡張Stream型を持っています。

個々の値、配列、イテラブル、ストリームまたはリアクティブストリームパブリッシャは、インスタンスメソッドとして追加および追加できます。

Stream stream = ReactiveSeq.of(1,2)
                           .filter(x -> x!=0)
                           .append(ReactiveSeq.of(3,4))
                           .filter(x -> x!=1)
                           .append(5)
                           .filter(x -> x!=2);

[私はcyclops-reactの主導的開発者です]

1
John McClean

あなた自身のconcatメソッドを書くのはどうですか?

public static Stream<T> concat(Stream<? extends T> a, 
                               Stream<? extends T> b, 
                               Stream<? extends T> args)
{
    Stream<T> concatenated = Stream.concat(a, b);
    for (Stream<T> stream : args)
    {
        concatenated = Stream.concat(concatenated, stream);
    }
    return concatenated;
}

これは少なくとも最初の例をもっと読みやすくします。

0
Felix S