You can easily get rid of complex operations such as “traversal, re-traversal, re-computation”, but there is much more to a Stream than that. This article will show you the ultimate Stream manipulation that will make grouping and summarizing collections easier and impress your colleagues.

A, Collectors

Java. Util. Stream. Collectors, starts the new JDK1.8 into a class. According to the class notes of the source code, you can see that Collectors implement various useful reduction operations, such as classifying types into new collections and summarizing elements according to different standards. Through the example, can let us see, a single line of code can handle such a powerful, complex function: summary, concatenation, cumulative calculation, grouping, etc.

Remember, don’t use it the wrong way. Yeahjava.util.stream.Collectors, notjava.util.Collections.

/** * Implementations of {@link Collector} that implement various useful reduction * operations, such as accumulating elements into collections, summarizing * elements according to various criteria, etc. * * <p>The following are examples of using the predefined collectors to perform * common mutable reduction tasks: * * <pre>{@code * // Accumulate names into a List * List<String> list = people.stream().map(Person::getName).collect(Collectors.toList()); * * // Accumulate names into a TreeSet * Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new)); * * // Convert elements to strings and concatenate them, separated by commas * String joined = things.stream() * .map(Object::toString) * .collect(Collectors.joining(", ")); * * // Compute sum of salaries of employee * int total = employees.stream() * .collect(Collectors.summingInt(Employee::getSalary))); * * // Group employees by department * Map<Department, List<Employee>> byDept * = employees.stream() * .collect(Collectors.groupingBy(Employee::getDepartment)); * * // Compute sum of salaries by department * Map<Department, Integer> totalByDept * = employees.stream() * .collect(Collectors.groupingBy(Employee::getDepartment, * Collectors.summingInt(Employee::getSalary))); * * // Partition students into passing and failing * Map<Boolean, List<Student>> passingFailing = * students.stream() * .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD)); * *}</pre> * * @since 1.8 */Copy the code

In other words, a Collectors Stream would be the ultimate operation for collections, including:

  • Type categorization: To classify the elements in a collection according to type and condition filtering and store them in a new collection of a specified type.
  • Group: Groups elements according to conditions, and SQLgroup byThe usage of “is similar to that of”.
  • Partition: the special case of grouping is essentially doing two groups, grouping the elements that meet the conditions and do not meet the conditions into two groupskeyRespectively,trueandfalsetheMap, so we can get a new set of matching and dismatching groups.
  • Maximum value: Finds the largest and smallest element by an attribute.
  • Summing up: Used to complete summing up calculation, data summary (total, total, minimum, maximum, average).
  • Concatenate: To concatenate elements according to some rule.

Ii. Actual combat exercises

1. Type classification

The elements in the Collection are filtered by type or condition and stored in a new Collection of the specified type, such as List, Map, Set, Collection or ConcurrentMap. The following methods are involved:

  • Collectors.toList()

  • Collectors.toMap()

  • Collectors.toSet()

  • Collectors.toCollection()

  • Collectors.toConcurrentMap()

It is usually used as an argument to the termination operator Cololect and is accompanied by the end of the stream.

It is often used to collect and screen the qualified data in the set (complex set) and store it in a new set of the corresponding type for subsequent practical business logic processing.

For example, to categorize the name type into a List

collection:

List<String> list = allPeoples.stream().map(People::getName).collect(Collectors.toList());
Copy the code

Group 2.

Grouping elements by conditions is similar to the SQL use of group by, and it is often recommended to use Java code for grouping to reduce database SQL stress.

Grouping involves the following methods:

  • Collectors. GroupingBy (…). : Common group.

  • Collectors. GroupingByConcurrent (…). : thread-safe grouping.

After grouping, a Map set is returned, in which key serves as the grouping object and value serves as the corresponding grouping result.

For example, considering that there may be peers in the People set, group the set by age:

Map<Integer, List<People>> groupingByAge = allPeoples.stream().collect(Collectors.groupingBy(People::getAge));
Copy the code

What if we don’t want to return Map value as List? It can actually be grouped as follows:

Map<Integer, Set<People>> groupingByAge2 = allPeoples.stream().collect(Collectors.groupingBy(People::getAge, Collectors.toSet()));
Copy the code

What happens when you consider synchronization security?

USES the thread safe grouping Collectors. GroupingByConcurrent (…). And then:

Map<Integer, List<People>> groupingByAge3 = allPeoples.stream().collect(Collectors.groupingByConcurrent(People::getAge));
Copy the code

3. The partition

Is the special case of grouping, use Collectors. PartitioningBy (…). Method to accomplish.

This method is essentially binary grouping, grouping the qualified and unqualified elements into two maps with keys true and false respectively, so that we can get a new set of matched and dismatched groups.

For example, the set People has both Chinese and English names, and the names are divided into two groups:

Map<Boolean, List<People>> partitioningByName = allPeoples.stream().collect(Collectors.partitioningBy(people -> people.getName().matches("^[a-zA-Z]*"))); List<People> englishNames = partitioningByname.get (true); List<People> chineseNames = partitioningbyname.get (false);Copy the code

4. The most value

Find the maximum or minimum value for an attribute, compare it using the Comparator interface, return an Optional object, and determine the maximum or minimum value with option.isPresent ().

The following methods are involved:

  • Collectors. MaxBy (…). : Maximum value.
  • Collectors. MinBy (…). : Minimum value.

For example, find the oldest and youngest People in the People set:

// Find the oldest person Optional<People> maxAgeOptional = allPeoples.stream().collect(Collectors.maxBy(Comparator.comparingInt(People::getAge))); People maxAgePeople = null; if (maxAgeOptional.isPresent()) { maxAgePeople = maxAgeOptional.get(); } // Find minimum age People Optional<People> minAgeOptional = allPeoples.stream().collect(Collectors.minBy(Comparator.comparingInt(People::getAge))); People minAgePeople = null; if (minAgeOptional.isPresent()) { minAgePeople = minAgeOptional.get(); }Copy the code

5. Add and summarize

It is used to perform cumulative calculation, data summary (total, sum, minimum, maximum, average) operations.

Computes the sum of an attribute in a collection, similar to the sum function in SQL.

The following methods are involved:

  • Collectors. SummingInt/Double/Long (…). : Sums according to an attribute.
  • Collectors. SummarizingInt/Double/Long (…). : Collects the total number, total number, minimum value, maximum value, and average value of an attribute.

For example, to sum up the salaries of all staff:

int salaryTotal = allPeoples.stream().collect(Collectors.summingInt(People::getSalary));
Copy the code

What if I wanted to get the overall salary data for all employees (total, total, minimum, maximum, average)?

Do you want multiple streams?

Of course, there is no such trouble, just Collectors. SummarizingInt method can be easily done.

/ / output: IntSummaryStatistics {count = 10, sum = 45000, min = 2000, business = 4500.000000, max=7000} IntSummaryStatistics intSummaryStatistics = allPeoples.stream().collect(Collectors.summarizingInt(People::getSalary));Copy the code

6. Connect

Concatenate the elements with some rule, resulting in a concatenation string.

The following methods are involved:

  • Collectors. Joining () : a direct string connection.
  • Joining (CharSequence delimiter) : following charactersdelimiterString concatenation.
  • Joining (CharSequence delimiter, CharSequence prefix, CharSequence suffix) : follows the prefixprefixThe suffixsuffixAnd to the characterdelimiterString concatenation.

For example, concatenate all the names in the People set with some kind of concatenation:

/ / output: XcbeyondNikiXiaoMing = xcbeyondNikiXiaoMing allPeoples.stream().map(People::getName).collect(Collectors.joining()); / / output: Xcbeyond,Niki,XiaoMing, XiaoMing, Lucy,Lily, Super Feixia, Ledi String namesStr2 = allPeoples.stream().map(People::getName).collect(Collectors.joining(",")); / / output: [xcBeyond,Niki,XiaoMing, XiaoMing, Lucy,Lily, Super Feixia, Ledi] String namesStr3 = allPeoples.stream().map(People::getName).collect(Collectors.joining(",", "[", "]"));Copy the code

Third, summary

This article, only for JDK1.8 Java. Util. Stream. The Collectors in the best operating separately, for example, does not involve nested, compound, superposition is used, the actual business scenarios may involve multiple operating under superposition and combination, on-demand flexible use required.

If you are familiar with these operations, you will be much more comfortable working with complex sets and logic. Especially grouping, summary, it is too good.

What are some of the most useful and fun end-user actions you’ve come across with JDK1.8?