How it works...

As we mentioned in the introduction of this recipe, the collect() method allows you to do a mutable reduction of the elements of a Stream. We call it a mutable reduction because the final result of the stream will be a mutable data structure, such as Map or List. The Stream class of the Java Concurrency API provides two versions of the collect() method.

The first one receives only one parameter that is an implementation of the Collector interface. This interface has seven methods, so you normally won't implement your own collectors. Instead of this, you will use the utility class Collectors, which has a lot of methods that return ready-to-use Collector objects for your reduce operations. In our example, we have used the following methods of the Collectors class:

  • groupingByConcurrent(): This method returns a Collector object that implements a group by operating with the elements of Stream in a concurrent way, generating Map as the resultant data structure. It receives as parameter an expression to obtain the value of the key used in the map from the element of the stream. It generates Map where the keys will be of the type returned by the parameter expression and the value will be a List of elements of the stream.
  • joining(): This method returns Collector that concatenates the elements of the stream into String. You can specify three CharSequence objects with a separator for the elements, a prefix of the final String, and a suffix of the final String.
  • partitioningBy(): This method returns Collector similar to the first one. It receives as parameter a Boolean expression with the elements of Stream and organizes the elements of the stream in two groups: the ones that meet the expressions and the ones that don't. The final result will be Map with a Boolean key and List of the type of elements of the stream as value.
  • toConcurrentMap(): This method returns Collector that generates ConcurrentMap in a concurrent way. It receives three parameters:
    • An expression to generate the key from an element of the stream
    • An expression to generate the value from an element of the stream
    • An expression to generate a value from two values when there are two or more elements with the same key

Collector has a set of Characteristics that define its behavior and can be defined or not for a specific collector. For us, the most important is the CONCURRENT one that indicates if the collector can work in a concurrent way or not. In this case, we can't take advantage of our multicore processor by creating only a parallel stream. If we use a collect operation with Collector, we have to also take into account the value of the CONCURRENT characteristic of that Collector. We will only have a concurrent reduction if the next three conditions are true:

  • The Stream is parallel (we have used parallelStream() of the parallel() methods in the stream)
  • The collector has the CONCURRENT characteristic
  • Either the stream is unordered, or the collector has the UNORDERED characteristic

In our case, groupingByConcurrent() and toConcurrentMap() return collectors which have the CONCURRENT characteristic and the joining() and partitionBy() methods return collectors that don't have such characteristics.

However, there's another version of the collect() method that can be used with parallel streams. This version of the collect() method receives the following three parameters:

  • A supplier function that generates a data structure of the type of the final result of the collect operation. With parallel streams, this function will be called as many times as there are threads executing the operation.
  • An accumulator function that receives a data structure and an element of the stream and makes the process of the element.
  • A combiner function that receives two data structures and generates a unique data structure of the same type.

You can use lambda expressions to implement these functions, but you can also implement the Supplier interface for the supplier function or the BiConsumer interface for the accumulator and combiner functions (always parameterized with the adequate data types). You can also use method references (Class::Method) if the input and output parameters are adequate. For example, we have used the List::new reference as the supplier function and the List::addAll method as the combiner function. We could use the List::add method as the accumulator function. There are more methods you can use as parameters to the collect() method.

The following screenshot shows the output of the groupingByConcurrent() operation:

The following screenshot shows the output of the toConcurrentMap() operation:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.116.59