Functional consumption of recreational services

Therefore, I was curious how the following code would be rewritten more efficiently with the java8 API.

public static List<FlightInfo> getResults(String origin,List<String> destinations) { final String uri = "https://api.searchflight.com/; List<FlightInfo> results = new LinkedList<FlightInfo>(); for(String destination:destinations) { RestTemplate restTemplate = new RestTemplate(); String params = getParams(origin,destination); FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class); results.add(result); } return results; } 

After executing this method, doing what it does, and I get a list of FLightInfo objects, I convert it to a stream and I will perform various conversions on it (group by etc.). Now it’s pretty good that this is a long job. In addition, it actually combines several rest calls into a web service, so I already have most of the data received at the time of my last call, but I would not process it before the whole method returns.

Is there a way to make all this more reactive? Can I immediately return the stream and perform operations on the data of the stream process, as it goes through the pipe, or is it too much to ask? How it will be done in Java 8. What

+6
source share
1 answer

Well, it all depends on when you need the result. If you want it to be consistent, this below is still worthy, since its lazy. But it will boil during terminal operation (say during collect ).

 public static Stream<FlightInfo> getResults(String origin,List<String> destinations) { final String uri = "https://api.searchflight.com/"; return destinations.stream().map(destination -> { RestTemplate restTemplate = new RestTemplate(); String params = getParams(origin,destination); FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class); return result; }) } 

Or I'll do it with destinations.stream().parallel() if I can. This is a reasonable result in most cases. But still he will not start processing it in parallel until the moment when you call the terminal operation for it. Which absolutely makes sense.

But it seems to me that you want a consumer producer. For what:

 public static CompletableFuture<List<FlightInfo>> getResults(String origin,List<String> destinations) { final String uri = "https://api.searchflight.com/"; List<CompletableFuture<FlightInfo>> collect = destinations .stream() .map(destination -> CompletableFuture.supplyAsync(() -> { RestTemplate restTemplate = new RestTemplate(); String params = getParams(origin,destination); FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class); return result; })).collect(Collectors.toList()); return sequence(collect); //line-1 } public static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> com) { return CompletableFuture.allOf(com.toArray(new CompletableFuture[com.size()])) .thenApply(v -> com.stream() .map(CompletableFuture::join) .collect(Collectors.toList()) ); } 

For simplicity , line-1 you can simply return collect instead of sequence(collect) . You can then iterate over the list to get each value.

But with sequence , you have one CompletableFuture object that you can worry about, after which you can check the values ​​immediately after completion.

+5
source

All Articles