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

时间:2017-08-13 07:03:28

标签: java java-8 java-stream

我的理解是,一旦执行终端操作(例如StreamforEach()),就会认为Java 8 count()被使用。

但是,下面的测试用例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);
}

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

2 个答案:

答案 0 :(得分:20)

执行终端操作后,将认为Stream已消耗。但是,即使多个中间操作也不应该针对同一个Stream实例执行,如Stream javadoc中所述:

  

流应该操作(仅调用中间或终端流操作)。例如,这排除了&#34;分叉&#34;流,相同的源提供两个或多个管道,或同一流的多个遍历。 如果流实现检测到正在重用流,则可能会抛出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);
}

答案 1 :(得分:11)

根据Stream Javadoc

  

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

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