【问题标题】:Using Java 8 Supplier in streams to achieve lazy evaluation在流中使用 Java 8 Supplier 实现惰性求值
【发布时间】:2019-01-27 04:49:03
【问题描述】:

我正在尝试在这样的流中使用供应商实现惰性评估

public static void main(String[] args) {

        Supplier<List<String>> expensiveListSupplier= getExpensiveList();
        getList().stream()
                .filter(s -> expensiveListSupplier.get().contains(s))
                .forEach(System.out::println);
    }

    private static Supplier<List<String>> getExpensiveList() {
        return () -> Stream
                .of("1", "2", "3")
                .peek(System.out::println)
                .collect(Collectors.toList());
    }

    private static List<String> getList() {
        return Stream.of("2", "3")
                .collect(Collectors.toList());
    }

但这会为列表中的每个元素调用 getExpensiveList() 方法。 我尽量不冗长,也不想写这样的东西,即添加不空的支票和东西。

public static void main(String[] args) {

        Supplier<List<String>> expensiveListSupplier = getExpensiveList();
        List<String> list = getList();
        if (!list.isEmpty()) {
            List<String> expensiveList = expensiveListSupplier.get();
            list.stream()
                    .filter(expensiveList::contains)
                    .forEach(System.out::println);
        }    
    }

    private static Supplier<List<String>> getExpensiveList() {
        return () -> Stream
                .of("1", "2", "3")
                .peek(System.out::println)
                .collect(Collectors.toList());
    }

    private static List<String> getList() {
        return Stream.of("2", "3")
                .collect(Collectors.toList());
    }

【问题讨论】:

  • 您的问题到底是什么?你能简化一下.. 我尽量不冗长,不想写这样的东西,即添加不空的检查和东西?
  • 你可以打电话给Set&lt;String&gt; expensiveStuff = new HashSet&lt;&gt;(getExpensiveList().get());。使用一个集合可以扩大到任何实际大小并具有恒定的运行时间。
  • 一个小提示:供应商实例在被使用后,防止它被垃圾收集和并行执行流,你可以检查这个链接logicbig.com/tutorials/core-java-tutorial/java-util-stream/…@Hoopje 指出更好地使用你的详细信息代码。
  • 在这里使用Supplier 有什么意义?您需要已经获取第一个元素的列表,因此与代码的复杂性相比,惰性很小。

标签: java java-8 java-stream lazy-evaluation


【解决方案1】:

您的第二个变体可以简化为

List<String> list = getList();
if(!list.isEmpty()) {
    list.stream()
        .filter(getExpensiveList().get()::contains)
        .forEach(System.out::println);
}

它使Supplier 的使用毫无意义,因为即使是对getExpensiveList() 的调用也只有在列表非空时才会完成。但另一方面,这是你无论如何都可以获得的最大懒惰,即当另一个列表为空时不请求昂贵的列表。无论哪种情况,当列表不为空时,将已经为第一个元素请求昂贵的列表。

如果昂贵的列表可以变大,你应该使用

List<String> list = getList();
if(!list.isEmpty()) {
    list.stream()
        .filter(new HashSet<>(getExpensiveList().get())::contains)
        .forEach(System.out::println);
}

相反,以避免重复的线性搜索。或者重新设计getExpensiveList() 以首先返回Set。

【讨论】:

  • 添加第二个代码 sn-p 的原因是为了解释我的问题(第一个 sn-p),而不是为了改进它。我认为您的回答不会为我的预期问题增加价值。无论如何感谢您的回答。
  • 这不是为流的每个元素调用getExpensiveList().get() 吗?
  • @Amardeep 我决定提供您的第二个代码 sn-p 的改进变体,因为它在您的问题中看起来更复杂,但实际上比您的第一个变体更简单。正如我在回答中已经解释的那样,您的第一个变体没有提供任何实际好处,即关于所需的懒惰。所以有一个变体比其他变体更简单但仍然没有任何缺点。如果这不是答案,那么您可能问错了问题。
  • @FedericoPeraltaSchaffner 否,expression::name 表单的方法引用将对表达式求值一次并捕获结果。另见What is the equivalent lambda expression for System.out::println...
  • @FedericoPeraltaSchaffner 是的,正如我在问题中所指出的那样。最重要的是,这就是提出这个问题的原因。
【解决方案2】:

我认为仅使用标准 Java 类是不可能的。但是您可以编写自己的惰性求值器并使用它,例如(未经测试):

public class LazyValue<T> implements Supplier<T> {
    private T value;
    private final Supplier<T> initializer;
    public LazyValue(Supplier<T> initializer) {
        this.initializer = initializer;
    } 
    public T get() {
        if (value == null) {
            value = initializer.get();
        }
        return value;
    }
}

还有其他可能性,例如见this question。

但要小心!如果添加惰性求值,则数据结构是可变的,因此如果在多线程环境(或并行流)中使用它,请添加同步。

但是,我会使用您的详细版本。它是做什么的,一目了然,而且它只长了四行。 (在实际代码中,我希望这四行是无关紧要的。)

【讨论】:

  • 我是这么认为的!!只是想检查一下是否有人想出了一个简洁实用的解决方案。
猜你喜欢
  • 1970-01-01
  • 2021-09-26
  • 2014-02-28
  • 1970-01-01
  • 1970-01-01
  • 2017-03-17
  • 2019-11-16
  • 1970-01-01
  • 2019-02-27
相关资源
最近更新 更多