I take it from here, actually many other places provide this implementation.
What I want to add is a test case and talking a little bit about how its working:
The idea is:
At line 2; we use the JDK method allOf to execute all CompletableFutures passed in. (clear enough, otherwise see the javadoc)
At line 3; we use thenApply, on the result CompletableFuture that is (from javadoc) :
"returns a new CompletionStage that, when this stage completes normally, is executed with this stage's result as the argument to the supplied function"Which means, when the allOf finish execution, the supplier parameter of thenApply will start execution.
Note, the parameter of the supplier is variable ignored, because we don't need it as it is of type Void.
At line 4, 5: what we need to do is to iterate over the completed CompletableFutures (that is granteed to be completed, as I said above) and get the result and aggregate them in a list, so the join here should not wait, as the result is already there.
Now we have a CompletableFuture of list of aggregated results that we can use. (see the test case below)