具有memoization的Java8流

时间:2016-09-11 21:08:18

标签: java java-8 java-stream memoization

如何在java8(可能是memoization进程)中重用已经通过流迭代计算的值?

如果流重复或再次提供,则将重新计算。在某些情况下,最好为该CPU时间交换内存。从头开始收集所有内容可能不是一个好主意,因为流用于查找满足谓词的第一个项。

Stream<Integer> all = Stream.of(1,2,3,4,5, ...<many other values>... ).
      map(x->veryLongTimeToComputeFunction(x));
System.out.println("fast find of 2"+all.filter(x->x>1).findFirst());

//both of these two lines generate a "java.lang.IllegalStateException: stream has already been operated upon or closed"
System.out.println("no find"+all.filter(x->x>10).findFirst());
System.out.println("find again"+all.filter(x->x>4).findFirst());

问题是Copy a stream to avoid "stream has already been operated upon or closed" (java 8)

5 个答案:

答案 0 :(得分:1)

为什么不在veryLongTimeToComputeFunction内使用记忆?您可以将memo cache作为参数添加到func。

答案 1 :(得分:1)

规范的内存中的Stream源是一个Collection。一个简单的,不能并行的Stream Memoization可以实现如下:

public static void main(String[] args) {
    Supplier<Stream<Integer>> s=memoize(
        IntStream.range(0, 10_000)
                 .map(x -> veryLongTimeToComputeFunction(x))
    );
    System.out.println("First item > 1  "+s.get().filter(x -> x>1 ).findFirst());
    System.out.println("First item > 10 "+s.get().filter(x -> x>10).findFirst());
    System.out.println("First item > 4  "+s.get().filter(x -> x>4 ).findFirst());
}
static int veryLongTimeToComputeFunction(int arg) {
    System.out.println("veryLongTimeToComputeFunction("+arg+")");
    return arg;
}

public static <T> Supplier<Stream<T>> memoize(BaseStream<T,?> stream) {
    Spliterator<T> sp=stream.spliterator();
    class S extends Spliterators.AbstractSpliterator<T> {
        ArrayList<T> mem=new ArrayList<>();
        S() { super(sp.estimateSize(), sp.characteristics()); }
        public boolean tryAdvance(Consumer<? super T> action) {
            return sp.tryAdvance(item -> {
                mem.add(item);
                action.accept(item);
            });
        }
    }
    S s=new S();
    return () -> Stream.concat(s.mem.stream(), StreamSupport.stream(s, false));
}

在向供应商请求下一个Stream之前,请务必完成Stream处理。

答案 2 :(得分:0)

我建议您将Stream收集到列表中,然后在列表的流中运行过滤器。

答案 3 :(得分:0)

流不是已保存,它们即将处理数据。

示例:你正在观看一个DVD,用java术语说dvd就像一个集合,从你的dvd播放器转移到你的电视的数据就是一个流。你无法保存流,但你可以刻录光盘,用java术语收集它。

还有其他选择:

  • 将流操作或谓词提取/重构为获取流作为参数并返回流的方法
  • 使用缓存框架:例如in Spring方法可以使用 @Cacheable 进行注释。第一次调用执行该方法,后续调用从缓存中获取结果达定义的时间
  • 如果您正在为长时间运行的任务寻找非阻塞执行,请查看RxJava

答案 4 :(得分:0)

Java 8流本质上是懒惰的。对流执行的操作按垂直顺序进行评估。 您可以使用以下代码实现您想要实现的目标:

Stream.of(1,2,3,4,5, ...<many other values>... )
    .map(x-> veryLongTimeToComputeFunction(x))
    .filter(x-> x > 1)
    .findFirst();

这将确保仅调用veryLongTimeToComputeFunction(),直到找不到匹配的第一个元素。之后,操作将终止。 在最坏的情况下,如果最后一个数字是符合条件的数字,那么将为所有数字调用veryLongTimeToComputeFunction。

此外,您可以将并行流与findAny()方法结合使用。它会加快表现。