有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

concat向流中添加两个Java8流或一个额外元素

我可以添加流或额外元素,如下所示:

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

我的问题是:

1)有什么好的理由说明concat是静态的吗?还是我缺少了一些等效的实例方法

2)无论如何,有没有更好的方法


共 (6) 个答案

  1. # 1 楼答案

    如果您不介意使用第三方库cyclops-react有一个扩展流类型,它允许您通过append/prepend操作符来实现这一点

    可以将单个值、数组、iterables、流或反应流发布器作为实例方法追加和前置

    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的首席开发者]

  2. # 2 楼答案

    您可以使用番石榴的^{}.^{}方法,这会导致流变平:

    Stream stream = Streams.concat(stream1, stream2, Stream.of(element));
    
  3. # 3 楼答案

    MyStreamEx库扩展了流API的功能。特别是,它提供了像appendprepend这样的方法来解决这个问题(它们在内部使用concat)。这些方法可以接受另一个流、集合或varargs数组。使用my library可以通过这种方式解决您的问题(注意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));
    

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

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

    如果为流添加静态导入。海螺流。对于,第一个示例可以写成如下:

    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(…)采集(浓度(…)。这两个静态方法可以实现如下(可选地与静态导入结合使用):

    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。这两种操作都会对程序的行为产生重大影响。但是,如果可读性比性能更重要,那么它可能仍然是一种非常有用的方法

  5. # 5 楼答案

    只要做:

    Stream.of(stream1, stream2, Stream.of(element)).flatMap(identity());
    

    其中identity()Function.identity()的静态导入

    将多个流连接到一个流与展平一个流相同

    然而,不幸的是,由于某些原因,Stream上没有flatten()方法,因此必须将flatMap()与identity函数一起使用

  6. # 6 楼答案

    不幸的是,这个答案可能没有什么帮助,但我对Java Lambda邮件列表进行了取证分析,看看是否能找到这种设计的原因。这就是我发现的

    开始时,有一个流的实例方法。康卡特(溪流)

    在邮件列表中,我可以清楚地看到该方法最初是作为一个实例方法实现的,正如Paul Sandoz在this thread中读到的,关于concat操作

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

    this other thread中可以看到,JDK 8的一些早期用户对concat实例方法与null参数一起使用时的行为提出了疑问

    不过,这表明,concat方法的设计正在讨论之中

    重构为流。康卡特(溪流,溪流)

    但是在没有任何解释的情况下,突然之间,这些方法被更改为静态方法,如this thread about combining streams中所示。这可能是唯一一条关于这一变化的邮件线索,但我还不清楚重构的原因。但是我们可以看到他们在did a commit中建议将concat方法从Stream移到helper类Streams

    重构为流。康卡特(溪流,溪流)

    后来it was moved againStreamsStream,但同样没有解释

    所以,归根结底,设计的原因对我来说并不完全清楚,我找不到一个好的解释。我想你仍然可以在邮件列表中问这个问题

    流连接的一些备选方案

    other thread by Michael Hixson讨论/询问组合/concat流的其他方法

    1. To combine two streams, I should do this:

      Stream.concat(s1, s2)
      

      not this:

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

      ... right?

    2. To combine more than two streams, I should do this:

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

      not this:

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

      ... right?