在java中,如何处理CompletableFutures并获得第一个需要完成的结果?

在java中,如何处理CompletableFutures并获得第一个需要完成的结果?,java,concurrency,java-8,completable-future,Java,Concurrency,Java 8,Completable Future,通常,对于一个完整的未来,我会调用thenApply或其他方法,以便在结果可用时立即执行某些操作。然而,我现在有一种情况,我想处理结果,直到我收到一个积极的结果,然后忽略所有进一步的结果 如果我只想得到第一个可用的结果,我可以使用CompletableFuture.anyOf(尽管我讨厌仅仅为了调用anyOf而将列表转换为数组)。但那不是我想要的。我想得到第一个结果,如果没有理想的结果,那么我想处理第二个可用的结果,依此类推,直到得到理想的结果 下面是一个简单的示例,它遍历所有结果并返回它找到的

通常,对于一个完整的未来,我会调用thenApply或其他方法,以便在结果可用时立即执行某些操作。然而,我现在有一种情况,我想处理结果,直到我收到一个积极的结果,然后忽略所有进一步的结果

如果我只想得到第一个可用的结果,我可以使用CompletableFuture.anyOf(尽管我讨厌仅仅为了调用anyOf而将列表转换为数组)。但那不是我想要的。我想得到第一个结果,如果没有理想的结果,那么我想处理第二个可用的结果,依此类推,直到得到理想的结果

下面是一个简单的示例,它遍历所有结果并返回它找到的第一个大于9的值。(请注意,这不是我真正的任务。这只是一个简单的示例。)

public Integer findFirstGt9(列出结果){
用于(可完成的未来结果:结果){
整数v=result.get();
如果(v>9)
返回v;
}
返回null;
}
当然,该示例从一开始就介绍了结果,而不是在结果完成时查看结果。这里有一个可以实现我想要的,但是代码要复杂得多的程序

public Integer findFirstGt9(List<CompletableFuture<Integer>> results) {
    AtomicInteger finalResult = new AtomicInteger();
    CountDownLatch latch = new CountDownLatch(results.size());
    for(CompletableFuture<Integer> result : results) {
        result.whenComplete((v,e) -> {
            if(e!=null) {
                Logger.getLogger(getClass()).error("",e);
            } else if(v > 9) {
                finalResult.set(v);
                while(latch.getCount() > 0)
                    latch.countDown();
                return;
            }
            latch.countDown();
        });
    }
    latch.await();

    if(finalResult.get() > 9)
        return finalResult.get();
    return null;
}    
public Integer findFirstGt9(列出结果){
AtomicInteger finalResult=新的AtomicInteger();
CountDownLatch latch=新的CountDownLatch(results.size());
用于(可完成的未来结果:结果){
结果。完成时((v,e)->{
如果(e!=null){
getLogger.getLogger(getClass())。错误(“,e);
}如果(v>9),则为else{
最终结果集(v);
while(lock.getCount()>0)
倒计时();
返回;
}
倒计时();
});
}
satch.wait();
if(finalResult.get()>9)
返回finalResult.get();
返回null;
}    
有一个api可以让我这么做吗

public Integer findFirstGt9(List<CompletableFuture<Integer>> results) {
    Iterator<Integer> resultIt = getResultsAsAvailable(results);
    for(; resultIt.hasNext();) {
        Integer v = resultIt.next();
        if(v > 9)
            return v;
    }
    return null;
}
public Integer findFirstGt9(列出结果){
迭代器resultIt=getresultsavaailable(结果);
对于(;resultIt.hasNext();){
整数v=resultIt.next();
如果(v>9)
返回v;
}
返回null;
}
或者更好:

public Integer findFirstGt9(List<CompletableFuture<Integer>> results) {
    return getFirstMatch(results, r -> {return r > 9;});
}
public Integer findFirstGt9(列出结果){
返回getFirstMatch(结果,r->{return r>9;});
}

我不知道JDK或其他地方有这样的API。你可以自己滚

如果未来已经完成,您可以利用这样一个事实(除此之外,
complete
)什么都不做

如果尚未完成,则设置
get()
和相关 方法设置为给定值

创建新的最终结果
CompletableFuture
。如果您的条件适用,则在您的每个期货中添加一个延续,尝试
完成此最终结果。这一未来将随着第一次成功而结束。但是,如果没有成功,那么显然需要
null
。您可以使用
allOf
创建
CompletableFuture
,也可以尝试使用
null
完成
最终结果

差不多

public static <T> CompletableFuture<T> firstOrNull(List<CompletableFuture<T>> futures, Predicate<T> condition) {
    CompletableFuture<T> finalResult = new CompletableFuture<>();
    // attempt to complete on success
    futures.stream().forEach(future -> future.thenAccept(successResult -> {
        if (condition.test(successResult))
            finalResult.complete(successResult);
    }));
    CompletableFuture<?> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
    all.thenRun(() -> {
        finalResult.complete(null);
    });
    return finalResult;
}
public static CompletableFuture firstOrNull(列出未来、谓词条件){
CompletableFuture finalResult=新的CompletableFuture();
//试图在成功后完成
futures.stream().forEach(future->future.thenept(successResult->{
if(条件测试(成功结果))
最终结果完成(成功结果);
}));
CompletableFuture all=CompletableFuture.allOf(futures.toArray(新的CompletableFuture[0]);
全部。然后运行(()->{
最终结果完成(空);
});
返回最终结果;
}
您需要支付无操作调用的开销


您可以根据需要将
null
更改为某些默认值,或以不同方式处理异常(
completeeexceptionally
)。您必须在完成时使用
句柄
,而不是上面的
然后接受
,才能访问
异常
,您可以使用以下解决方案:

public static <T> CompletableFuture<T> anyMatch(
    List<? extends CompletionStage<? extends T>> l, Predicate<? super T> criteria) {

    CompletableFuture<T> result=new CompletableFuture<>();
    Consumer<T> whenMatching=v -> { if(criteria.test(v)) result.complete(v); };
    CompletableFuture.allOf(l.stream()
        .map(f -> f.thenAccept(whenMatching)).toArray(CompletableFuture<?>[]::new))
    .whenComplete((ignored, t) ->
        result.completeExceptionally(t!=null? t: new NoSuchElementException()));
    return result;
}
public static <T> CompletableFuture<T> anyMatch(
    List<? extends CompletionStage<? extends T>> l, Predicate<? super T> criteria) {

    CompletableFuture<T> result=new CompletableFuture<>();
    Consumer<T> whenMatching=v -> { if(criteria.test(v)) result.complete(v); };
    CompletableFuture.allOf(l.stream()
        .map(f -> f.thenAccept(whenMatching)).toArray(CompletableFuture<?>[]::new))
    .whenComplete((ignored, t) ->
        result.completeExceptionally(t!=null? t: new NoSuchElementException()));
    return result;
}
List<CompletableFuture<Integer>> list=Arrays.asList(
    CompletableFuture.supplyAsync(()->5),
    CompletableFuture.supplyAsync(()->{throw new RuntimeException(); }),
    CompletableFuture.supplyAsync(()->42),
    CompletableFuture.completedFuture(0)
);
anyMatch(list, i -> i>9)
    .thenAccept(i->System.out.println("got "+i))
    // optionally chain with:
    .whenComplete((x,t)->{ if(t!=null) t.printStackTrace(); });