在流中使用Java 8 Supplier实现延迟评估

时间:2018-08-21 03:33:42

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

我试图在像这样的流中使用Supplier实现懒惰的评估

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

2 个答案:

答案 0 :(得分: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

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

但是,我将使用您的详细版本。现在可以清楚地知道它的作用,而且只长了四行。 (在实际代码中,我希望这四行无关紧要。)

答案 1 :(得分:2)

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

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