将两个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是静态的。 Ifconcat是一个实例方法,上面的例子将更容易阅读:

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)无论如何,有没有更好的方法呢?


#1 热门回答(149 赞)

不幸的是,这个答案可能很少或没有任何帮助,但我对Java Lambda邮件列表进行了取证分析,看看我是否能找到这种设计的原因。这就是我发现的。
**最初有一个Stream.concat(Stream)**的实例方法
在邮件列表中,我可以清楚地看到该方法最初是作为实例方法实现的,你可以阅读Paul Sandoz在this thread中关于concat操作的内容。

他们在其中讨论了流可能无限的情况以及在这些情况下串联意味着什么的问题,但我不认为这是修改的原因。

你可以在this other thread中看到JDK 8的一些早期用户在使用null参数时询问了concat实例方法的行为。

但是,other thread显示了concat方法的设计正在讨论中。
重构为Streams.concat(Stream,Stream)
但是没有任何解释,突然之间,方法被改为静态方法,正如你在this thread about combining streams中看到的那样。这可能是唯一能够揭示这一变化的邮件主题,但对于我来说,确定重构的原因尚不清楚。但是我们可以看到他们建议将他们从Stream中移出concat方法并进入帮助者类Streams
重构为Stream.concat(Stream,Stream)
后来,it was moved againStreamsStream,但又一次,没有解释。

所以,底线,设计的原因对我来说并不完全清楚,我找不到一个好的解释。我想你仍然可以在邮件列表中提出这个问题。
流连接的一些替代方案
other thread by Michael Hixson讨论/询问其他组合/连接流的方法

要组合两个流,我应该这样做:Stream.concat(s1,s2)
 不是这个:Stream.of(s1,s2).flatMap(x - > x)
 ... 对?要组合两个以上的流,我应该这样做:Stream.of(s1,s2,s3,...)。flatMap(x - > x)
 不是这个:Stream.of(s1,s2,s3,...)。reduce(Stream.empty(),Stream :: concat)
 ... 对?


#2 热门回答(112 赞)

如果你addStatic importsforStream.concatandStream.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);

现在代码明显缩短了。但是,我同意可读性没有改善。所以我有另一个解决方案。

在很多情况下,Collectors可用于扩展流的功能。将两个收集器放在底部,这两个例子可以写成如下:

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

你想要的语法和上面的语法之间的唯一区别是,你必须用withcollect(concat(...))替换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));
}

当然,这个解决方案有一个缺点,应该提到。收集是一个消耗流的所有元素的最终操作。最重要的是,collectorconcat创建一个在链中使用的intermediateArrayListeach时间。这两种操作都会对程序的行为产生重大影响。但是,如果可读性比性能更重要,它可能仍然是一种非常有用的方法。


#3 热门回答(12 赞)

MyStreamExlibrary扩展了Stream API的功能。特别是它提供了解决此问题的方法,如appendprepend(内部使用concat)。这些方法可以接受另一个流或集合或varargs数组。使用我的库你的问题可以通过这种方式解决(注意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));

顺便说一下,还有一个快捷方式,用于你的操作:

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