2016-04-14 3 views
5

通常、CompletableFutureを使用すると、結果が利用可能になるとすぐに、thenApplyまたは他のメソッドを呼び出して何かを実行します。しかし、今私は肯定的な結果を得てからすべての結果を無視するまで結果を処理したいという状況があります。Javaでは、CompletableFuturesをどのように処理して、完了した最初の望ましい結果が得られますか?

私は、CompletableFuture.anyOfを使うことができました(リストを配列に変換してanyOfを呼び出す必要はありません)。しかし、それは私が望むものではありません。私は最初の結果を得たい、それが望ましい結果をもたない場合、望ましい結果が得られるまで、2番目に利用可能な結果を​​処理したいと思います。ここで

はすべての結果を通過し、それが9よりも大きい見つかった最初の値を返す単純な例を示します(これは私の本当の仕事ではないことに注意してください。これは単純な例です。)

public Integer findFirstGt9(List<CompletableFuture<Integer>> results) { 
    for(CompletableFuture<Integer> result : results) { 
     Integer v = result.get(); 
     if(v > 9) 
      return v; 
    } 
    return 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(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(List<CompletableFuture<Integer>> results) { 
    return getFirstMatch(results, r -> {return r > 9;}); 
} 

答えて

2

あなたは、以下のソリューションを使用することができます。

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

基本的な原理はPillar’s answerと同じであり、しかし、いくつかの違いがあります。

  • 総称署名がより柔軟です。
  • CompletableFuture.allOfに必要な配列の作成は、ソース先物へのフォローアップアクションの登録と組み合わせられます。副作用として、allOfアクションのハンドラは、元の先物のみではなく、結果を完了するためのすべての試行の完了に依存します。これにより、実際に望ましい依存関係が明示的になります。そうすれば、thenAcceptをすべてthenAcceptAsyncに置き換えても機能します。
  • この解決策は、結果が一致しない場合にnullを返すのではなく、NoSuchElementExceptionで完了します。少なくとも1つの未来が例外的に完了し、結果が一致して正常終了しなかった場合、発生した例外の1つが中継されます。

あなたは

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(); }); 
でそれを試してみて
3

私は他の場所でJDKまたはでそのようなAPIを知りません。自分でロールすることができます。

CompletableFuture#complete(およびcompleteExceptionally)は、未来が既に完了している場合は何もしません。

まだ完了していない場合は、指定された値にget()および関連 メソッドから返される値が設定されます。

新しい最終結果CompletableFutureを作成します。あなたの条件に当てはまる場合は、complete最終結果を試す各先物に継続を追加してください。その未来は最初の成功で終わるでしょう。しかし、成功しない場合は、明らかにnullが必要です。 CompletableFutureallOfに作成して、completeの最終結果を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; 
} 

よう

何か。

nullをデフォルト値に変更したり、例外を別の方法で処理したりすることができます(エラーが発生するとすぐにcompleteExceptionally)。 Exceptionにアクセスするには、thenAcceptの代わりにwhenCompleteまたはhandleを使用する必要があります。

関連する問題