有没有办法重用一个流?

Fra*_*sco 59 java reusability java-stream

我正在学习新的Java 8功能,在尝试使用streams(java.util.stream.Stream)和收集器时,我意识到流不能使用两次.

有没有办法重复使用它?

Han*_*k D 70

如果您想要重用流,可以将流表达式包装在供应商中,并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());
Run Code Online (Sandbox Code Playgroud)

  • 这就是Hank D说"效果"的原因.它仍然更干净,更可重复使用代码. (19认同)
  • 这不会重用流.供应商只需在每次调用时创建一个新流. (17认同)
  • 为什么要创建一个供应商来创建流,而不是只在你想要这样一个流的地方调用`someList.stream()`? (4认同)
  • 同意,如果有理由相信供应商可能有必要进行其他举动,那么这并不是一个坏主意,但我确实认为,即使“后果”也有些误导。重用流的好处是不必再次创建它,对吗?不确定使用“ Collection :: stream()”创建(检索?)流时要完成多少工作,但是当我用Google搜索它时,我想要的效果(可能没有很好的需求)是最小化对象实例化。 (2认同)
  • 来自`Supplier` java doc:**没有要求每次调用供应商时都返回一个新的或不同的结果。** (2认同)
  • 有趣的建议,但我什至认为这可能不一定更干净。在上面的例子中,它是可读的并且很好,但是如果 `sup.get()` 调用在代码中更远的地方呢?然后我认为 `sup.get()` 是一种误导,因为它向普通读者暗示该操作可能比它更便宜。所以,我认为`someList.stream()` 是更干净、更可重用的形式。 (2认同)

Vin*_*igh 62

文档:

应该仅对一个流进行操作(调用中间或终端流操作).

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

所以答案是否定的,流不是要重用.

  • @Armali 你是对的,这些术语听起来并不具体。那是因为“Stream”是一个接口,而我上面引用的是“预期的”行为。然而,实施者在实施过程中可能会违反合同。“Stream”的用户应该期待我上面引用的行为。这就是为什么流*应该*只被操作一次。至于抛出异常,那是由实现者决定的。 (3认同)

And*_*ejs 25

正如其他人所说,"不,你不能".

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

所以代替:

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();
Run Code Online (Sandbox Code Playgroud)

您可以:

// Can also be DoubleSummaryStatistics from mapToDouble()

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

stats.getAverage();
stats.getMin();
stats.getMax();
Run Code Online (Sandbox Code Playgroud)

  • 对这个问题的补充很好。我没有意识到这一点,事实证明它非常方便! (3认同)

Tag*_*eev 8

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
Run Code Online (Sandbox Code Playgroud)

如果您不想实现该流,则在某些情况下,可以使用多种方法一次对同一流进行处理。例如,您可以参考这个这个问题的详细资料。


Lii*_*Lii 5

正如其他人指出的那样,流对象本身无法重用。

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

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

例子:

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());
}
Run Code Online (Sandbox Code Playgroud)

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

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

例子:

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() ...
}
Run Code Online (Sandbox Code Playgroud)