Java 有没有办法重用Stream?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36255007/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:38:26  来源:igfitidea点击:

Is there any way to reuse a Stream?

javajava-streamreusability

提问by Francisco

I'm learning the new Java 8 features, and while experimenting with streams (java.util.stream.Stream) and collectors, I realized that a stream can't be used twice.

我正在学习 Java 8 的新特性,在试验流 ( java.util.stream.Stream) 和收集器时,我意识到一个流不能被使用两次。

Is there any way to reuse it?

有什么办法可以重复使用吗?

回答by Vince Emigh

From the documentation:

文档

A stream should be operated on (invoking an intermediate or terminal stream operation) only once.

A stream implementation may throw IllegalStateException if it detects that the stream is being reused.

一个流应该只被操作一次(调用一个中间或终端流操作)。

如果流实现检测到流正在被重用,它可能会抛出 IllegalStateException。

So the answer is no, streams are not meant to be reused.

所以答案是否定的,流不应该被重用。

回答by Tagir Valeev

The whole idea of the Stream is that it's once-off. This allows you to create non-reenterable sources (for example, reading the lines from the network connection) without intermediate storage. If you, however, want to reuse the Stream content, you may dump it into the intermediate collection to get the "hard copy":

Stream 的整个想法是一次性的。这允许您创建不可重新输入的源(例如,从网络连接读取行)而无需中间存储。但是,如果您想重用 Stream 内容,则可以将其转储到中间集合中以获取“硬拷贝”:

Stream<MyType> stream = // get the stream from somewhere
List<MyType> list = stream.collect(Collectors.toList()); // materialize the stream contents
list.stream().doSomething // create a new stream from the list
list.stream().doSomethingElse // create one more stream from the list

If you don't want to materialize the stream, in some cases there are ways to do several things with the same stream at once. For example, you may refer to thisor thisquestion for details.

如果您不想具体化流,在某些情况下,有一些方法可以同时对同一个流执行多项操作。例如,您可以参考thisthisquestion了解详细信息。

回答by Hank D

If you want to have the effect of reusing a stream, you might wrap the stream expression in a Supplier and call myStreamSupplier.get()whenever you want a fresh one. For example:

如果您想要重用流的效果,您可以将流表达式包装在一个供应商中,并myStreamSupplier.get()在需要新的时调用。例如:

Supplier<Stream<String>> sup = () -> someList.stream();
List<String> nonEmptyStrings = sup.get().filter(s -> !s.isEmpty()).collect(Collectors.toList());
Set<String> uniqueStrings = sup.get().collect(Collectors.toSet());

回答by Andrejs

As others have said, "no you can't".

正如其他人所说,“不,你不能”。

But it's useful to remember the handy summaryStatistics()for many basic operations:

但是记住summaryStatistics()许多基本操作的方便是很有用的:

So instead of:

所以而不是:

List<Person> personList = getPersons();

personList.stream().mapToInt(p -> p.getAge()).average().getAsDouble();
personList.stream().mapToInt(p -> p.getAge()).min().getAsInt();
personList.stream().mapToInt(p -> p.getAge()).max().getAsInt();

You can:

你可以:

// Can also be DoubleSummaryStatistics from mapToDouble()

IntSummaryStatistics stats = personList.stream()
                                       .mapToInt(p-> p.getAge())
                                       .summaryStatistics();

stats.getAverage();
stats.getMin();
stats.getMax();

回答by BabaNew

Come to think of it, this will of "reusing" a stream is just the will of carry out the desired result with a nice inline operation. So, basically, what we're talking about here, is what can we do to keep on processing after we wrote a terminal operation?

想想看,这种“重用”流的意愿只是通过一个很好的内联操作来实现预期结果的意愿。所以,基本上,我们在这里谈论的是,在我们编写了一个终端操作之后,我们可以做些什么来继续处理?

1) if your terminal operation returns a collection, the problem is solved right away, since every collection can be turned back into a stream (JDK 8).

1) 如果您的终端操作返回一个集合,则问题立即解决,因为每个集合都可以转回流 (JDK 8)。

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .collect(Collectors.toList())
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

2) if your terminal operations returns an optional, with JDK 9 enhancements to Optional class, you can turn the Optional result into a stream, and obtain the desired nice inline operation:

2) 如果您的终端操作返回一个optional,使用 JDK 9 对 Optional 类的增强,您可以将 Optional 结果转换为流,并获得所需的漂亮的内联操作:

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .findAny()
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

3) if your terminal operation returns something else, i really doubt that you should consider a stream to process such result:

3)如果您的终端操作返回其他内容,我真的怀疑您是否应该考虑使用流来处理此类结果:

List<Integer> l=Arrays.asList(5,10,14);
        boolean allEven=l.stream()
            .filter(nth-> nth>5)
            .allMatch(nth-> nth%2==0);
        if(allEven){
            ...
        }

回答by Lii

As others have noted the stream object itself cannot be reused.

正如其他人所指出的,流对象本身不能被重用。

But one way to get the effect of reusing a stream is to extract the stream creation code to a function.

但是获得重用流效果的一种方法是将流创建代码提取到函数中

You can do this by creating a method or a function object which contains the stream creation code. You can then use it multiple times.

您可以通过创建包含流创建代码的方法或函数对象来完成此操作。然后您可以多次使用它。

Example:

例子:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

    // The normal way to use a stream:
    List<String> result1 = list.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    // The stream operation can be extracted to a local function to
    // be reused on multiple sources:
    Function<List<Integer>, List<String>> listOperation = l -> l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    List<String> result2 = listOperation.apply(list);
    List<String> result3 = listOperation.apply(Arrays.asList(1, 2, 3));

    // Or the stream operation can be extracted to a static method,
    // if it doesn't refer to any local variables:
    List<String> result4 = streamMethod(list);

    // The stream operation can also have Stream as argument and return value,
    // so that it can be used as a component of a longer stream pipeline:
    Function<Stream<Integer>, Stream<String>> streamOperation = s -> s
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i);

    List<String> result5 = streamOperation.apply(list.stream().map(i -> i * 2))
        .filter(s -> s.length() < 7)
        .sorted()
        .collect(toCollection(LinkedList::new));
}

public static List<String> streamMethod(List<Integer> l) {
    return l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());
}


If, on the other hand, you already have a stream object which you want to iterate over multiple times, then you must save the content of the stream in some collection object.

另一方面,如果您已经有一个想要迭代多次的流对象,那么您必须将流的内容保存在某个集合对象中。

You can then get multiple streams with the same content from than collection.

然后,您可以从集合中获取具有相同内容的多个流。

Example:

例子:

public void test(Stream<Integer> stream) {
    // Create a copy of the stream elements
    List<Integer> streamCopy = stream.collect(toList());

    // Use the copy to get multiple streams
    List<Integer> result1 = streamCopy.stream() ...
    List<Integer> result2 = streamCopy.stream() ...
}

回答by Bill Burdick

The Functional Java libraryprovides its own streams that do what you are asking for, i.e. they're memoized and lazy. You can use its conversion methods to convert between Java SDK objects and FJ objects, e.g. Java8.JavaStream_Stream(stream)will return a reusable FJ stream given a JDK 8 stream.

功能的Java库提供了自己的流是你所问的,即它们是memoized和懒惰。您可以使用它的转换方法在 Java SDK 对象和 FJ 对象之间进行转换,例如,Java8.JavaStream_Stream(stream)给定 JDK 8 流,将返回可重用的 FJ 流。