9

I need to create a generic array of CompletableFuture objects so that I can pass it to the CompletableFuture.allOf method to get one CompletableFuture to sync the threads. But since it is a generic I cannot create it. One obvious solution is to create a List and then call toArray on it but it'll be inefficient. Are there any better methods? Here is my code:

// Current solution: 
List<CompletableFuture<List<ReportComparable>>> newReports = new ArrayList<>();

// Loop and add CompletableFuture objects to this list

// Collect all the retrieved objects here(Sync Threads).
    try {
        List<List<ReportComparable>> newReps = CompletableFuture.allOf((CompletableFuture<?>[]) newReports.toArray()).get();
    } catch (InterruptedException | ExecutionException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
ayushgp
  • 4,891
  • 8
  • 40
  • 75

1 Answers1

6

There are several wrong assumptions in your question

  • You can’t do (CompletableFuture<?>[]) newReports.toArray(). The parameterless toArray() method will return Object[] and the casting attempt will cause a ClassCastException. Only the toArray(T[]) method accepting an existing array will return an array of the same type, which brings you back to square zero

  • CompletableFuture.allOf returns a CompletableFuture<Void>, so you can’t call get() on it and expect to get a List<List<ReportComparable>>. You have to assemble the result list yourself after completion.

  • There is no problem creating an array of a generic class, when all type arguments are wildcards. So

    CompletableFuture<?>[] array = new CompletableFuture<?>[100];
    

    works.

  • When you have an arbitrary number of elements, adding them to a List and invoking toArray on it, isn’t necessarily inefficient. The alternative, dealing with an array and an index manually, is doing the same as an ArrayList, but error prone. The single copying step of toArray is rarely performance relevant.

    Also keep in mind, since the CompletableFuture.allOf returns CompletableFuture<Void> you might need the List<CompletableFuture<List<ReportComparable>>> anyway to be able to construct the desired List<List<ReportComparable>> after the completion.

    On the other hand, when you have a fixed number of arguments, you may call the varargs method CompletableFuture.allOf directly without manual array creation.


But when all you want to do with the CompletableFuture returned by allOf, is to call get() immediately, the “wait for all” operation doesn’t have any benefit anyway.

You get the same effect implicitly when querying the individual CompletableFuture instances via get() or join() and adding the result to your resulting List, which you have to do anyway after completion, as CompletableFuture.allOf does not do that for you.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • allOf would be executed in parallel while looping over individual objects calling get() would be sequential right? Also if I remove the get call on this CompletableFuture and loop over the list calling get() and add the returned items to my list would that be faster than calling get directly on each object? – ayushgp Jun 07 '17 at 03:37
  • 1
    Calling `allOf` has no influence on how the individual futures complete. That has been set when you created them. In either case, you are waiting for the completion of all futures, so it takes exactly the same time. – Holger Jun 07 '17 at 10:44
  • Oh alright. I thought that looping over futures calling get would be worse that wrapping them in one future and calling get on it. Thanks! Also I have one more question for CompletableFuture, if you can answer it: https://stackoverflow.com/questions/44409962/throwing-exception-from-completablefuture/44410084 – ayushgp Jun 07 '17 at 10:48