惰性地调用java流中的方法



我有一个开销很大的方法,我只想在流中必要时调用它。下面是一个例子:

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

目标是根据input值从myList中找到目标MyObject,但如果它不在myListONLY中,则它将调用expensive()返回一个更大的列表并从那里查看。

上面的例子没有这样做,因为看起来Stream.concat在消耗所有myList之前已经调用了expensive()

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

return myList.stream().filter(o -> o.hasName(input)).findFirst().or(
() -> expensive().stream().filter(o -> o.hasName(input)).findFirst());

但是我将不得不重复过滤器和其余的两次。

是否有更好的解决方案,甚至是一个单一的线性流?

您可以通过连接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();
}

测试:

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

输出:

case 1: Optional[MyObject[s=a]]
expensive() called
case 2: Optional.empty

或者你可以这样做:

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

另一种可能更容易理解的方法是在一个单独的方法中提取流逻辑:

private static Optional<MyObject> findInternal(String input, List<MyObject> myList) {
return myList.stream().filter(o -> o.hasName(input)).findFirst();
}

,然后简单地调用两次:

public static Optional<MyObject> findTarget(String input, List<MyObject> myList) {
return findInternal(input, myList).or(() -> findInternal(input, expensive()));
}

最新更新