延迟调用 java Stream 中的方法

use*_*188 3 java lazy-evaluation java-stream

我有一个昂贵的方法,我只想在流中必要时调用它。这是一个例子:

public static Optional<MyObject> findTarget(String input, List<MyObject> myList) {
    return Stream.concat(myList.stream(), expensive().stream()).filter(o -> o.hasName(input)).findFirst();
}
Run Code Online (Sandbox Code Playgroud)

目标是根据值MyObject找到目标,但如果它不在ONLY 中,那么它将调用返回更大的列表并从那里查找。myListinputmyListexpensive()

上面的示例没有这样做,因为它似乎在Stream.concat消耗expensive()所有myList.

我能想到的一个丑陋的解决方案是分两步完成,例如:

return myList.stream().filter(o -> o.hasName(input)).findFirst().or(
    () -> expensive().stream().filter(o -> o.hasName(input)).findFirst());
Run Code Online (Sandbox Code Playgroud)

但随后我将不得不重复过滤器和其余部分两次。

有没有更好的解决方案,甚至是单个 Stream 衬里可以做到这一点?

モキャ*_*ャデ 7

您可以通过连接Supplier<List<MyObject>>而不是 来延迟评估List<MyObject>

public static Optional<MyObject> findTarget(String input, List<MyObject> myList) {
    List<Supplier<List<MyObject>>> concat = List.of(() -> myList, () -> expensive());
    return concat.stream()
        .flatMap(supplier -> supplier.get().stream())
        .filter(o -> o.hasName(input))
        .findFirst();
}
Run Code Online (Sandbox Code Playgroud)

测试:

record MyObject(String s) {
    public boolean hasName(String in) {
        return s.equals(in);
    }
}

static List<MyObject> expensive() {
    System.out.println("expensive() called");
    return List.of(new MyObject("z"));
}

public static void main(String[] args) {
    List<MyObject> myList = List.of(new MyObject("a"));
    System.out.println("case 1: " + findTarget("a", myList));
    System.out.println("case 2: " + findTarget("x", myList));
}
Run Code Online (Sandbox Code Playgroud)

输出:

case 1: Optional[MyObject[s=a]]
expensive() called
case 2: Optional.empty
Run Code Online (Sandbox Code Playgroud)

或者你可以这样做:

public static Optional<MyObject> findTarget(String input, List<MyObject> myList) {
    return Stream.of(
            (Supplier<List<MyObject>>) () -> myList,
            (Supplier<List<MyObject>>) () -> expensive())
        .flatMap(supplier -> supplier.get().stream())
        .filter(o -> o.hasName(input))
        .findFirst();
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,早期版本的 Java 中存在一个 [bug](/sf/ask/3240224081/),其中“flatMap()”只是部分惰性的。但我认为在这种情况下它仍然会避免昂贵的供应商。 (2认同)