If you add static imports for Stream.concat And Stream.of the first example could be written as follows:
Stream stream = concat(stream1, concat(stream2, of(element)));
Import static methods with generic names can result in code that becomes difficult to read and maintain ( namespace pollution ). So, it might be best to create your own methods static with more meaningful names. However, for the proof I will stay with this name.
public static Stream concat(Stream extends T> lhs, Stream extends T> rhs) {
return Stream.concat(lhs, rhs);
}
public static Stream concat(Stream extends T> lhs, T rhs) {
return Stream.concat(lhs, Stream.of(rhs));
}
With these two static methods (optionally in combination with static imports), the two examples could be written as follows:
Stream stream = concat(stream1, concat(stream2, element));
Stream stream = concat(
concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
element)
.filter(x -> x!=2);
The code is now significantly shorter. However, I agree that readability has not improved. So I have another solution.
In many situations, Collectors can be used for extend the functionality of flows. With the two Collector at the bottom, the two examples could be written as follows:
Stream stream = stream1.collect(concat(stream2)).collect(concat(element));
Stream stream = stream1
.filter(x -> x!=0)
.collect(concat(stream2))
.filter(x -> x!=1)
.collect(concat(element))
.filter(x -> x!=2);
The only difference between the desired syntax and the above syntax is that you need to replace concat (…) with collect (concat (…)) . The two static methods can be implemented as follows (optionally used in conjunction with static imports):
private static Collector combine(Collector collector, Function super R, ? extends S> function) {
return Collector.of(
collector.supplier(),
collector.accumulator(),
collector.combiner(),
collector.finisher().andThen(function));
}
public static Collector> concat(Stream extends T> other) {
return combine(Collectors.toList(),
list -> Stream.concat(list.stream(), other));
}
public static Collector> concat(T element) {
return concat(Stream.of(element));
}
Of course there is a drawback with this solution that should be mentioned. collect it is a final operation that consumes all elements of the stream. Furthermore, the collector concat create an intermediate ArrayList whenever it is used in the chain. Both can have a significant impact on the behavior of your program. However, if readability is more important than performance however, it could be a very useful approach.