流操作类别

流操作分为两大类,中间操作和终端操作,以及两个子类,无状态和有状态。

中级操作:

中间操作总是很懒惰,比如简单的 Stream.map。在实际使用流之前不会调用它。这可以很容易地验证:

Arrays.asList(1, 2 ,3).stream().map(i -> {
    throw new RuntimeException("not gonna happen");
    return i;
});

中间操作是流的公共构建块,链接在源之后,通常后跟触发流链的终端操作。

终端操作

终端操作是触发流消耗的原因。一些比较常见的是 Stream.forEachStream.collect。它们通常放在一系列中间操作之后,几乎总是渴望

无国籍行动

无状态意味着处理每个项目时没有其他项目的上下文。无状态操作允许对存储器进行高效的流处理。像 Stream.mapStream.filter 这样的操作不需要关于流的其他项目的信息被认为是无状态的。

有状态的操作

有状态意味着每个项目的操作取决于流的(某些)其他项目。这需要保留一个状态。有状态操作可能会破坏长流或无限流。像 Stream.sorted 这样的操作需要在发出任何项目之前处理整个流,这将在足够长的项目流中中断。这可以通过长流来证明( 运行风险自负 ):

// works - stateless stream
long BIG_ENOUGH_NUMBER = 999999999;
IntStream.iterate(0, i -> i + 1).limit(BIG_ENOUGH_NUMBER).forEach(System.out::println);

由于 Stream.sorted 的状态,这将导致内存不足:

// Out of memory - stateful stream
IntStream.iterate(0, i -> i + 1).limit(BIG_ENOUGH_NUMBER).sorted().forEach(System.out::println);