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);
コードは大幅に短くなりました。ただし、可読性が向上していないことに同意します。だから私は別の解決策を持っています。
多くの状況で、コレクタを使用してストリームの機能を拡張できます。下部に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を作成します。どちらの操作も、プログラムの動作に大きな影響を与える可能性があります。ただし、読みやすさがパフォーマンスよりも重要な場合は、それでも非常に役立つアプローチになる可能性があります。