Some of the _Stream_ common interface methods were introduced earlier and are not difficult to understand, but the use of _Stream_ goes beyond that. In this section, we will continue to use _Stream_ as an example to describe the protocol operation of a stream.
A specification operation, also known as a collapse operation, is a process by which all elements are aggregated into a summary result through a join action. Summing up elements, finding a maximum or minimum, finding the total number of elements, and converting all elements to a list or set are all protocol operations. The _Stream_ library has two generic protocol operations reduce() and collect(), as well as some specialized protocol operations designed to simplify writing, such as sum(), Max (), min(), count(), and so on.
While protocol operations such as Max or min are well understood (at least semantically), we focus on reduce() and collect(), where there is some magic.
Versatile reduce ()
The reduce operation can generate a value from a set of elements. Sum (), Max (), min(), count(), and so on are reduce operations. The method definition of reduce() can be overridden in three ways:
-
Optional<T> reduce(BinaryOperator<T> accumulator)
-
T reduce(T identity, BinaryOperator<T> accumulator)
-
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
Function definitions get longer, but the semantics remain the same, with multiple arguments just to indicate initial values or to specify how the results of multiple parts can be combined when executed in parallel. The most common scenario for reduce() is to generate a value from a bunch of values. Using such a complex function to find a maximum or minimum value, you do not think the designer is sick. It’s not, because “big” and “small” or “sum” sometimes have different meanings.
Need: Find the longest word in a list. Here “big” means “long”.
The code above picks out the longest word _love_, where _Optional_ is a container of values to avoid the trouble of _null_ values. You can use stream. Max (Comparator
comparator) to achieve the same effect, but reduce() exists for a reason.
The above code label (2) maps the I. String to the length, II., and adds the current sum. This is obviously a two-step operation, and using the reduce() function to combine the two steps into one can help improve performance. If you want to use a combination of map() and sum(), that’s fine.
Reduce () is good at generating a value, but what if you want to generate a collection from _Stream_ or complex objects like _Map_? Collect () Is the ultimate weapon!
Collect ()
Quite literally, if you find a feature not found in the _Stream_ interface, chances are you can implement it through the collect() method. Collect () is the most flexible of the _Stream_ interface methods, and learning it is a true introduction to Java functional programming. Let’s start with a few warm-up examples:
The above code illustrates how to convert _Stream_ to _List_, Set_, and _Map, respectively. Although the code semantics are clear, we still have a few questions:
-
What does function.identity () do?
-
What does String::length mean?
-
What is _Collectors_?
Static and default methods for the interface
Function_ is an interface, so what does function.identity () mean? This is explained in two ways:
-
Java 8 allows concrete methods to be added to interfaces. There are two specific methods in the interface, the _default_ method and the _static_ method. Identity () is a static method of the _Function_ interface.
-
Function.identity() returns a Lambda expression object whose output is the same as the input, equivalent to a Lambda expression of the form t -> t.
Does the above explanation make you wonder more? Don’t ask me why there are specific methods in the interface, or tell me that you think t -> t is more intuitive than the identity() method. I’ll tell you that the _default_ method in an interface is a dead end, and that in Java 7 and prior to that it was difficult or even impossible to add new abstract methods to a defined interface because all classes that implemented the interface had to be re-implemented. Imagine adding a stream() abstract method to the _Collection_ interface? The _default_ method is designed to solve this awkward problem by implementing the newly added method directly in the interface. Now that you’ve introduced the _default_ method, why not add the _static_ method to avoid specialized utility classes?
Method references
Syntax forms such as String:: Length are called method references and are used as an alternative to certain forms of Lambda expressions. If all a Lambda expression is about is calling an existing method, a method reference can replace the Lambda expression. Method references can be subdivided into four categories:
The collector
I’m sure you’ve lost your enthusiasm for learning Java functional programming, but unfortunately, there’s more to it. But you can’t blame the Stream library for this, because what you’re trying to implement is inherently complex.
A Collector is a tool interface (class) tailored for the stream.collect () method. Consider what it takes to convert a _Stream_ into a container (or a _Map_). We need at least two things:
-
What is the target container? Is it _ArrayList_ or _HashSet_ or is it _TreeMap_.
-
How are new elements added to the container? List.add() or map.put ().
If the specification is done in parallel, you also need to tell _collect()_ 3. How to combine multiple partial results into one.
Combining the above analysis, the collect() method is defined as
R collect(Supplier
Supplier, BiConsumer
Accumulator, BiConsumer
combiner), and the three parameters correspond to the above three analyses in sequence. Collect () :
R collect(Collector
collector). The Collectors_ utility class generates a variety of commonly used _collectors through static methods. For example, if you want to specify _Stream_ as _List_, you can do this in one of two ways:
Normally, instead of manually specifying the three parameters of _collect(), we call _collect(Collector
collector)_ methods, and most of the Collector_ objects in the parameter are obtained directly from the _Collectors_ utility class. In fact, the behavior of the incoming collector determines the behavior of collect().
Use collect() to generate a Collection
The method of converting _Stream_ to a container through the collect() method was mentioned earlier, so here’s a summary. Converting _Stream_ to _List_ or _Set_ is a common operation, so the _Collectors_ tool already provides us with a collector, which can be done with the following code:
The above code will do most of the work, but since the interface type is returned, we don’t know what container type the library actually chooses, and sometimes we may want to manually specify the actual container type. This requirement can be accomplished through the Collectors. ToCollection (Supplier
collectionFactory) method.
The code (3) above specifies that the protocol result is _ArrayList_ and (4) specifies that the protocol result is _HashSet_. Everything you wanted.
Use collect() for the string join
This must be a welcome feature for string concatenation using a collector generated by Collectors. Joining (), which says goodbye to the _for_ loop. The Collectors. Joining () method has three rewriting forms, corresponding to three different joining modes. Needless to say, the code is unforgettable.
Collect () Can do more
In addition to using the collector already wrapped by the _Collectors_ utility class, we can customize the collector or call collect(Supplier
Supplier, BiConsumer
Accumulator, BiConsumer
combiner) method, collect any form of information you want. However, the _Collectors_ utility class should meet most of our requirements, so please check the documentation before implementing it manually. There will be opportunities to share more _Collectors_ operation cases