如果为 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));
}
使用这两个静态方法(可以选择与静态导入结合使用),可以按如下方式编写这两个示例:
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);
代码现在明显更短。但是,我同意可读性没有提高。所以我有另一个解决方案。
在很多情况下,收集器可用于扩展流的功能。在底部有两个收集器的情况下,两个示例可以写如下:
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(...))。这两个静态方法可以按如下方式实现(可以选择与静态导入结合使用):
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 是使用流的所有元素的最终操作。最重要的是,每次在链中使用中间 ArrayList 时,收集器 concat 都会创建一个中间 ArrayList。这两种操作都会对程序的行为产生重大影响。但是,如果可读性比性能更重要,它可能仍然是一种非常有用的方法。