有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

带记忆的Java8流

如何在java8(可能是一个记忆过程)中重用已经通过流上的迭代计算出的值

如果流被复制或再次提供,它将被重新计算。在某些情况下,最好用内存换取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) 个答案

  1. # 1 楼答案

    流并不意味着被保存,它们将处理数据

    例如:你正在看一张dvd,用java语言来说,dvd就像一个集合,从dvd播放机传输到电视的数据是一个流。你不能保存流,但是你可以刻录cd,用java术语来说就是收集它

    还有其他选择:

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

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

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

    这将确保仅在未找到匹配的第一个元素之前调用veryLongTimeToComputeFunction()。之后,手术将终止。 在最坏的情况下,如果最后一个数字与标准匹配,则会对所有数字调用很长时间的computeFunction

    还可以将并行流与findAny()方法结合使用。这将加快表演速度

  3. # 3 楼答案

    标准内存流源是一个集合。一个简单的、不具备并行能力的流存储器可以实现如下:

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

    在向供应商请求下一个流之前,注意完成流处理

  4. # 4 楼答案

    为什么不在veryLongTimeToComputeFunction内使用备忘录?可以将memo cache作为func的参数

  5. # 5 楼答案

    我建议将你的Stream收集到一个列表中,然后在列表流上运行你的过滤器