什么时候认为Java 8 Stream被消费了?

cra*_*eld 18 java java-8 java-stream

我的理解是,Stream一旦执行终端操作(例如forEach()或)count(),就认为Java 8 被消耗.

但是,multipleFilters_separate下面的测试用例抛出一个IllegalStateException即使filter是一个懒惰的中间操作,只是作为两个语句调用.然而,我可以将两个过滤操作链接到一个语句中并且它可以工作.

@Test(expected=IllegalStateException.class)
public void multipleFilters_separate() {
    Stream<Double> ints = Stream.of(1.1, 2.2, 3.3);
    ints.filter(d -> d > 1.3);
    ints.filter(d -> d > 2.3).forEach(System.out::println);
}

@Test
public void multipleFilters_piped() {
    Stream<Double> ints = Stream.of(1.1, 2.2, 3.3);
    ints.filter(d -> d > 1.3)
        .filter(d -> d > 2.3)
        .forEach(System.out::println);
}
Run Code Online (Sandbox Code Playgroud)

从这开始,我假设Stream在第一个使用它的语句之后被认为是消耗的,无论该语句是否调用终端操作.听起来不错吗?

Era*_*ran 20

Stream一旦执行终端操作,就认为A 被消耗.但是,即使是多个中间操作也不应该针对同一个Stream实例执行,如Streamjavadoc 中所述:

应该仅对一个流进行操作(调用中间或终端流操作).例如,这排除了"分叉"流,其中相同的源提供两个或更多个管道,或者同一个流的多个遍历.如果流实现检测到正在重用流,则它可能会抛出IllegalStateException.但是,由于某些流操作可能返回其接收器而不是新的流对象,因此可能无法在所有情况下检测重用.

在中间流操作的情况下,您应该调用上一个操作Stream返回的下一个操作:

public void multipleFilters_separate() {
    Stream<Double> ints = Stream.of(1.1, 2.2, 3.3);
    ints = ints.filter(d -> d > 1.3);
    ints.filter(d -> d > 2.3).forEach(System.out::println);
}
Run Code Online (Sandbox Code Playgroud)

  • @craigcaulfield,就像我说的那样(如果你没有链接调用),你必须保持对每个`Stream`操作返回的`Stream`的引用,并在新的`Stream`上调用下一个操作. (5认同)

Joe*_*e C 11

根据StreamJavadoc:

应该仅对一个流进行操作(调用中间或终端流操作).例如,这排除了"分叉"流,其中相同的源提供两个或更多个管道,或者同一个流的多个遍历.IllegalStateException如果检测到流正在被重用,则可能会抛出流实现.但是,由于某些流操作可能返回其接收器而不是新的流对象,因此可能无法在所有情况下检测重用.

在您的情况下,对filter自身的调用是检测到将您的流分成两个不同的流的尝试.它不是在添加终端操作后等待并导致问题,而是执行先发制人的攻击,以便从任何堆栈跟踪中清楚地了解您的问题所在.