170

次のように、ストリームまたは追加の要素を追加できます。

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);

私の質問は:

concat1)静的である正当な理由はありますか? または、欠落している同等のインスタンスメソッドがありますか?

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

4

8 に答える 8

170

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

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

メーリング リストでは、concat 操作に関する Paul Sandoz のこのスレッドを読むことができるように、メソッドが最初はインスタンス メソッドとして実装されていたことを明確に確認できます。

その中で彼らは、ストリームが無限になる可能性がある場合に発生する可能性のある問題と、そのような場合に連結が何を意味するかについて説明していますが、それが変更の理由ではないと思います.

この別のスレッドで、JDK 8 の初期のユーザーの一部が、null 引数を使用した場合の concat インスタンス メソッドの動作について質問したことがわかります。

ただし、この別のスレッドは、concat メソッドの設計が議論中であったことを明らかにしています。

Streams.concat(Stream,Stream) にリファクタリング

しかし、何の説明もなく、突然、メソッドが静的メソッドに変更されました。これは、ストリームの結合に関するこのスレッドで確認できます。これはおそらく、この変更について少し光を当てる唯一のメール スレッドですが、リファクタリングの理由を判断するのに十分なほど明確ではありませんでした. しかし、メソッドをヘルパー クラスから出し入れすることを提案するコミットを行ったことがわかります。concatStreamStreams

Stream.concat(Stream,Stream) にリファクタリング

その後、からに再び移動されましたが、これについても説明がありません。StreamsStream

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

ストリーム連結の代替手段

Michael Hixson によるこの他のスレッドでは、ストリームを結合/連結する他の方法について議論/質問しています

  1. 2 つのストリームを結合するには、次のようにします。

    Stream.concat(s1, s2)
    

    これではない:

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

    ... 右?

  2. 2 つ以上のストリームを組み合わせるには、次のようにする必要があります。

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

    これではない:

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

    ... 右?

于 2014-03-30T07:41:27.963 に答える
128

Stream.concatStream.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);

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


多くの状況で、コレクタを使用してストリームの機能を拡張できます。下部に2 つのCollectorがあるため、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を作成します。どちらの操作も、プログラムの動作に大きな影響を与える可能性があります。ただし、読みやすさがパフォーマンスよりも重要な場合は、それでも非常に役立つアプローチになる可能性があります。

于 2014-03-30T08:04:14.310 に答える
3

Guava の方法を使用すると、平坦化されたストリームが得られます。Streams.concat(Stream<? extends T>... streams)

Stream stream = Streams.concat(stream1, stream2, Stream.of(element));
于 2017-09-18T10:30:18.307 に答える
1

サードパーティのライブラリを使用してもかまわない場合、cyclops-reactには拡張された Stream タイプがあり、append / prepend 演算子を介してそれを行うことができます。

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

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の主任開発者です]

于 2017-03-10T15:48:17.477 に答える
0

独自の concat メソッドを作成してみませんか?

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

これにより、少なくとも最初の例がより読みやすくなります。

@Legna が指摘したように、Stream::concat へのネストされた呼び出しが原因で、StackOverflowError が非常に速く発生する可能性があります。

したがって、問題を修正し、非常にきれいに見える別のバージョンを次に示します。

public static <T> Stream<T> concat(final Stream<? extends T>... args)
{
    return args == null ? Stream.empty()
                        : Stream.of(args).flatMap(Function.identity());
}
于 2017-08-16T12:06:58.717 に答える