流操作类别
流操作分为两大类,中间操作和终端操作,以及两个子类,无状态和有状态。
中级操作:
中间操作总是很懒惰,比如简单的 Stream.map
。在实际使用流之前不会调用它。这可以很容易地验证:
Arrays.asList(1, 2 ,3).stream().map(i -> {
throw new RuntimeException("not gonna happen");
return i;
});
中间操作是流的公共构建块,链接在源之后,通常后跟触发流链的终端操作。
终端操作
终端操作是触发流消耗的原因。一些比较常见的是 Stream.forEach
或 Stream.collect
。它们通常放在一系列中间操作之后,几乎总是渴望。
无国籍行动
无状态意味着处理每个项目时没有其他项目的上下文。无状态操作允许对存储器进行高效的流处理。像 Stream.map
和 Stream.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);