Three functional programming related articles have been updated
-
Is the functional programming Stream interface really that good?
-
JDK1.8 upgrade so long! What are the protocol operations for a Stream?
-
Why is Java functional programming stream.collect () so popular?
Focus on tech! Focus on system architecture, high availability, high performance, high concurrency technology sharing
A few days ago, I thought I’d put you off learning about Java functional programming, but unfortunately, it’s more complicated. 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 turn a Stream into a container (or Map). We need at least two things:
-
What is the target container? Is it an ArrayList or a HashSet, or a TreeMap.
-
How are new elements added to the container? List.add() or map.put (). If the specification is carried out in parallel, you also need to tell collect().
-
How to combine multiple partial results into one.
Combined with 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 tool class generates various common Collectors using static methods. For example, a Stream can be specified as a List in one of two ways:
Instead of manually specifying the three parameters of collect(), call collect(Collector
Collector) method, and collector objects in the parameter are obtained directly from the Collectors tool 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. It is a common operation to convert _Stream_ to _List_ or _Set_, so the Collectors are already provided by the Collectors tool. This can be done using 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 collector. 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() to generate a Map
As mentioned earlier, the Stream behind it depends on some kind of data source, which can be an array, a container, etc., but not a Map. It is possible to generate a Map from a Stream in reverse, but the fundamental reason we need to figure out what the Map’s key and value represent is because we need to figure out what to do. Collect () usually results in a Map in three cases:
-
With Collectors generated by Collectors. ToMap (), you need to specify how to generate Map keys and values.
-
Use Collectors. PartitioningBy () generated by the collector, the second partition on elements for operation when used.
-
The collector generated by Collectors.groupingby () is used when performing group operations on elements.
Case 1: Collectors generated using toMap(), which is the most straightforward, and mentioned in the previous example, is a parallel method to Collectors. The following code shows converting the student list to a Map composed of < student, GPA>. It’s very intuitive, no need to say more.
Case 2: collector generated using partitioningBy(). This case applies when elements in a Stream are split into complementary intersecting parts based on some binary logic (conditional or not), such as gender, grade, and so on. The following code shows how students are divided into passing or failing grades.
** Case 3: ** Uses the collector generated by groupingBy(), which is a more flexible case. Like the _group BY_ statement in SQL, _groupingBy() groups data by an attribute, and elements with the same attribute are mapped to the same _key_ in the _Map. The following code shows how employees are grouped by department:
This is just the most basic use of grouping. Sometimes grouping alone is not enough. _group BY_ is used in SQL to assist other queries, such as
-
Start by grouping employees by department
-
Then count the number of employees in each department.
Java library designers have this in mind, and an enhanced version of groupingBy() satisfies this need. The enhanced groupingBy() allows us to group elements before performing certain operations, such as summing, counting, averaging, type conversion, and so on. A Collector that groups elements first is called an upstream Collector, and a Collector that performs other operations later is called a Downstream Collector.
Does the logic of the above code look more and more like SQL? Highly unstructured. To go further, downstream collectors can also include further downstream collectors, which is not a trick added for the sake of showmanship, but a practical need for the scene. Consider a scenario where employees are grouped by department. If we wanted the name (string) of each Employee instead of individual Employee objects, we could do this as follows:
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 Collectors encapsulated by the Collectors tool class, you can customize Collectors or call collect(Supplier Supplier, BiConsumer
Accumulator, BiConsumer
combiner) method, collect any form of information you want. However, the Collectors tool class can meet most of our requirements. Please check the documentation before manually implementing it.