有没有办法重用一个流?

时间:2016-03-28 02:22:39

标签: java java-stream reusability

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

有没有办法重复使用它?

7 个答案:

答案 0 :(得分:59)

如果您想要重用流,可以将流表达式包装在供应商中,并在需要新的时调用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());

答案 1 :(得分:54)

来自documentation

  

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

     

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

所以答案是否定的,流不是要重复使用。

答案 2 :(得分:22)

正如其他人所说,&#34;不,你不能&#34;。

但是,对于许多基本操作来说,记住方便的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();

你可以:

// Can also be DoubleSummaryStatistics from mapToDouble()

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

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

答案 3 :(得分:6)

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

import dataset
import configs
db_host = configs.hosts['server']
db_url = 'mysql://%s:%s@%s/%s?charset=utf8' % (db_host['user'], db_host['password'], db_host['ip'], db_host['database'])
db = dataset.connect(db_url)

如果您不想实现流,在某些情况下,有一些方法可以同时使用相同的流执行多项操作。例如,您可以参考thisthis问题了解详情。

答案 4 :(得分:1)

来想一想,这将重新使用&#34;流是通过良好的内联操作实现所需结果的意愿。所以,基本上,我们在这里谈论的是,在我们编写终端操作后,我们可以做些什么来继续处理?

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)如果终端操作返回可选,对Optional类进行JDK 9增强,则可以将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)如果你的终端操作返回别的东西,我真的怀疑你应该考虑一个流来处理这样的结果:

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

答案 5 :(得分:1)

正如其他人所说,流对象本身无法重复使用。

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

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

示例:

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());
}

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

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

示例:

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() ...
}

答案 6 :(得分:0)

Functional Java library提供了自己的流,这些流可以满足您的要求,即被记住并且很懒。您可以使用其转换方法在Java SDK对象和FJ对象之间进行转换,例如给定JDK 8流,Java8.JavaStream_Stream(stream)将返回可重用的FJ流。