首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >复制一个流,避免“流已被操作或被关闭”

复制一个流,避免“流已被操作或被关闭”
EN

Stack Overflow用户
提问于 2014-05-26 06:25:00
回答 10查看 77.7K关注 0票数 146

我想复制一个Java 8流,这样我可以处理它两次。我可以collect作为一个列表,并从中获得新的流;

代码语言:javascript
复制
// doSomething() returns a stream
List<A> thing = doSomething().collect(toList());
thing.stream()... // do stuff
thing.stream()... // do other stuff

但我认为应该有一种更有效/更优雅的方式。

有没有一种方法可以复制流而不将其转换为集合?

我实际上是在处理一个Either流,所以我想先处理左投影,然后再转到右投影,然后再用另一种方法处理。有点像这样(到目前为止,我不得不使用toList技巧)。

代码语言:javascript
复制
List<Either<Pair<A, Throwable>, A>> results = doSomething().collect(toList());

Stream<Pair<A, Throwable>> failures = results.stream().flatMap(either -> either.left());
failures.forEach(failure -> ... );

Stream<A> successes = results.stream().flatMap(either -> either.right());
successes.forEach(success -> ... );
EN

回答 10

Stack Overflow用户

发布于 2014-05-26 08:00:32

我认为你关于效率的假设是一种倒退。如果您只使用数据一次,那么您将获得巨大的效率回报,因为您不必存储它,并且streams为您提供了强大的“循环融合”优化,使您可以通过管道高效地流动整个数据。

如果您想重用相同的数据,那么根据定义,您必须生成两次(确定性的)或存储它。如果它恰好已经在一个集合中,那就太好了;那么迭代两次是很便宜的。

我们在设计中用"forked streams“做了实验。我们发现,支持这一点是有实际成本的;它以牺牲不常见的情况为代价,增加了常见情况(使用一次)的负担。最大的问题是如何处理“当两个管道不以相同的速率消耗数据时会发生什么”。现在,你又回到了缓冲状态。这是一个明显没有分量的特性。

如果您希望对同一数据进行重复操作,请存储该数据,或者将您的操作构建为消费者并执行以下操作:

代码语言:javascript
复制
stream()...stuff....forEach(e -> { consumerA(e); consumerB(e); });

您还可以研究一下RxJava库,因为它的处理模型更适合这种“流分叉”。

票数 100
EN

Stack Overflow用户

发布于 2015-06-19 01:34:44

您可以使用带有Supplier的局部变量来设置流管道的公共部分。

来自http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/

重用Streams

Java 8流不能重用。只要调用任何终端操作,流就会关闭:

Stream stream = Stream.of("d2","a2","b1","b3","c") .filter(s -> s.startsWith("a"));stream.anyMatch(s -> true);// ok stream.noneMatch(s -> true);//在同一个流上的anyMatch之后调用noneMatch的异常导致以下异常: java.lang.IllegalStateException:流已经在java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)处的com.winterbe.java8.Streams5.test7(Streams5.java:38)处的com.winterbe.java8.Streams5.main(Streams5.java:28)处的java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)处操作或关闭

为了克服这一限制,我们必须为我们想要执行的每个终端操作创建一个新的流链,例如,我们可以创建一个流供应商来构造一个已经设置了所有中间操作的新流:

Supplier streamSupplier = () -> Stream.of("d2","a2","b1","b3","c") .filter(s -> s.startsWith("a"));streamSupplier.get().anyMatch(s -> true);// ok streamSupplier.get().noneMatch(s -> true);// ok

每次对get()的调用都会构造一个新的流,我们将在该流上保存以调用所需的终端操作。

票数 89
EN

Stack Overflow用户

发布于 2018-03-05 14:50:19

使用Supplier为每个终止操作生成流。

代码语言:javascript
复制
Supplier<Stream<Integer>> streamSupplier = () -> list.stream();

无论何时需要该集合的流,都可以使用streamSupplier.get()来获取新的流。

示例:

  1. streamSupplier.get().anyMatch(predicate);
  2. streamSupplier.get().allMatch(predicate2);
票数 35
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23860533

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档