Using Streams

If you’re familiar with the Java 8 Streams API, you might consider fulfilling this contract using Stream s:

You will be given a Stream of objects to process.

of objects to process. In return, you will supply a Stream of processing results.

of processing results. Because the supplied Stream may be infinite, you will never call any terminal method on that Stream (e.g. to gather its contents into a collection)

may be infinite, you will never call any terminal method on that (e.g. to gather its contents into a collection) You will define the processing of values using the Streams API’s map and filter functions.

and functions. If you call out to an external service, that service will return a Stream ; you will use the Stream API’s flatMap function to integrate these results into your results stream.

For example, here’s some code that accepts a Stream of search engine queries, and returns a Stream of search engine results:

public Stream doSearchEngineLookups(Stream queries) { return queries.map(this::parseQuery).flatMap(this::getSearchEngineResults); } private SearchEngineQuery parseQuery(String query) { // Parse the query string into a search engine query } private Stream getSearchEngineResults(SearchEngineQuery query) { // Call out to search engine to find results matching query }

The only problem with this is that when the client of doSearchEngineLookups attempts to do something with the Stream of values you’ve returned, any thread working on the stream will block whenever getSearchEngineResults blocks obtaining the next value to return in its Stream of results. It would be nice to have a non-blocking processing pipeline – perhaps we can use CompletableFuture to accomplish this?

Using CompletableFutures

Here’s how using CompletableFutures to fulfil our contract might look: