Various usages of Java8--Stream (2): collect, Collectors

Methods in Collectors: 

 

 Among them, the first three are commonly used: put the elements in the stream into a collection, group, and toMap. Below we introduce the use of these methods one by one.

Base class: Student

public class Student {
    private Integer id;

    private String name;

    private String className;

    private Double score;
 
    private Long timeStamp;
}

1. Put the elements in the stream into a collection: 

1.1 toCollection(Supplier<C> collectionFactory) : 

    public static <T, C extends Collection<T>> Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
        return new CollectorImpl<>(collectionFactory, Collection<T>::add,
                                   (r1, r2) -> { r1.addAll(r2); return r1; },
                                   CH_ID);
    }

Example: 

List<Student> studentList = new ArrayList<>();

LinkedList<Integer> collect = studentList.stream().map(Student::getId).collect(Collectors.toCollection(LinkedList::new));
        

1.2 toList()、toSet():

Go directly to the example: 

List<Integer> list = studentList.stream().map(Student::getId).collect(Collectors.toList());

Set<String> nameSet = studentList.stream().map(Student::getName).collect(Collectors.toSet());

2. Grouping

2.1 groupingBy It has three overloaded methods: 

    public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function classifier) {
        return groupingBy(classifier, toList());
    }

    public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function classifier, Collector downstream) {
        return groupingBy(classifier, HashMap::new, downstream);
    }

    public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function classifier, Supplier mapFactory, Collector downstream) {
        ......
    }

The first method only needs a grouping parameter classifier, and the result is automatically saved in a map internally. The key of each map is '?' type (that is, the result type of the classifier), and the value is a list, which is stored in the belonging elements of this group. But it actually calls the second method -- Collector defaults to list. The second method actually calls the third method, and the default Map generation method is HashMap. The third method is the real and complete grouping logic processing.

 Example: 

Stream<Student> stream = studentList.stream();

Map<Double, List<Student>> m1 = stream.collect(Collectors.groupingBy(Student::getScore));

Map<Double, Set<Student>> m2 = stream.collect(Collectors.groupingBy(Student::getScore, Collectors.toSet()));

Map<Double, Set<Student>> m3 = stream.collect(Collectors.groupingBy(Student::getScore,TreeMap::new, Collectors.toSet()));
        

2.2  groupingByConcurrent  

Returns a concurrent Collector to perform "group by" operation on input elements of type T. There are also three overloaded methods, which are basically the same as groupingBy. 

2.3 partitioningBy

This method divides the elements in the stream into two parts according to the results of the given verification rules, and puts them into a map to return. The key of the map is Boolean type, and the value is a collection of elements

Two overloaded methods: 

    public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
        return partitioningBy(predicate, toList());
    }

    public static <T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
                                                    Collector<? super T, A, D> downstream) {
        ......
    }

As can be seen from the above overloaded method, partitioningBy is similar to groupingBy, except that the key type of the map generated by partitioningBy can only be Boolean.

Example: 

Stream<Student> stream = studentList.stream();

Map<Boolean, List<Student>> m4 = stream.collect(Collectors.partitioningBy(stu -> stu.getScore() > 60));

Map<Boolean, Set<Student>> m5 = stream.collect(Collectors.partitioningBy(stu -> stu.getScore() > 60, Collectors.toSet()));

3、toMap

The toMap method saves the keys and values ​​generated by the given key generator and value generator into a map and returns them. The generation of keys and values ​​depends on the elements. You can specify the processing plan when duplicate keys appear and the map to save the results. .

toMap also has three overloaded methods: 

public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function keyMapper,Function valueMapper) {
    return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}

public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function keyMapper,Function valueMapper,BinaryOperator mergeFunction) {
    return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function keyMapper,Function valueMapper,BinaryOperator mergeFunction,Supplier mapSupplier){
    ......
}

The three overloaded methods are finally implemented by calling the third method. The first method specifies the key duplication processing method and the map generation method by default; while the second method specifies the map generation method by default. Users can customize the handling method of key duplication.

Example: 

Map<Integer, Student> map1 = stream.collect(Collectors.toMap(Student::getId, v->v));
Map<Integer, String> map2 = stream.collect(Collectors.toMap(Student::getId, Student::getName, (a, b)->a));
Map<Integer, String> map3 = stream.collect(Collectors.toMap(Student::getId, Student::getName, (a, b)->a, HashMap::new));
The use of toConcurrentMap is basically the same as toMap, except that toConcurrentMap is used to handle concurrent requests, and the map it generates is ConcurrentHashMap

4. Element splicing and joining

Three overloaded methods: 

  1. joining() : no delimiter and suffix, directly spliced
  2. joining(CharSequence delimiter) : specify the delimiter between elements
  3. joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix): Specify the delimiter and the prefix and suffix of the entire string .
Stream<String> stream = Stream.of("1", "2", "3", "4", "5", "6");
String s = stream.collect(Collectors.joining(",", "prefix", "suffix"));
        

Before joining, Stream must be of type Stream<String>

5. Type conversion

mapping: This mapping is to first map each element in the stream, that is, type conversion, and then induce the new element with a given Collector. Similar to the map method of Stream.

collectingAndThen: After the induction action ends, the induction result is reprocessed.

Stream<Student> stream = studentList.stream();

List<Integer> idList = stream.collect(Collectors.mapping(Student::getId, Collectors.toList()));

Integer size = stream.collect(Collectors.collectingAndThen(Collectors.mapping(Student::getId, Collectors.toList()), o -> o.size()));

6. Polymerization

  1. counting: Same as stream.count()
  2. minBy: Same as stream.min()
  3. maxBy: Same as stream.max()
  4. summingInt: 
  5. summingLong:
  6. summingDouble:
  7. averagingInt:
  8. averagingLong:
  9. averagingDouble:
        Long count = stream.collect(Collectors.counting());
        stream.count();
        stream.collect(Collectors.minBy((a,b)-> a.getId() - b.getId()));
        stream.min(Comparator.comparingInt(Student::getId));
        stream.collect(Collectors.summarizingInt(Student::getId));
        stream.collect(Collectors.summarizingLong(Student::getTimeStamp));
        stream.collect(Collectors.averagingDouble(Student::getScore));

7、reducing

The reducing method has three overloaded methods, which actually correspond to the three reduce methods in Stream. The two can be used interchangeably, and their functions are exactly the same. They also perform statistical induction on the elements in the stream.

public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) {
    ......
}

public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) {
    ......
}

public static <T, U> Collector<T, ?, U> reducing(U identity,Function mapper, BinaryOperator<U> op) {
    ......
}

Example:

List<String> list2 = Arrays.asList("123","456","789","qaz","wsx","edc");

Optional<Integer> optional = list2.stream().map(String::length).collect(Collectors.reducing(Integer::sum));

Integer sum1 = list2.stream().map(String::length).collect(Collectors.reducing(0, Integer::sum));

Integer sum2 = list2.stream().limit(4).collect(Collectors.reducing(0, String::length, Integer::sum));

extension: 

In actual application, more complex groupingBy, mapping, toMap nesting and combination may be used to process data in multi-level grouping. like: 

Stream<Student> stream = studentList.stream();
// 根据score分组,并提取ID作为集合元素
Map<Double, List<Integer>> map1 = stream.collect(Collectors.groupingBy(Student::getScore, Collectors.mapping(Student::getId, Collectors.toList())));
// 根据score分组, 并将ID和name组成map作为元素
Map<Double, Map<Integer, String>> map2 = stream.collect(Collectors.groupingBy(Student::getScore, Collectors.toMap(Student::getId, Student::getName)));
// 先根据score分组,再根据name进行二次分组
Map<Double, Map<String, List<Student>>> map3 = stream.collect(Collectors.groupingBy(Student::getScore, Collectors.groupingBy(Student::getName)));

Of course, we can also set the combination conditions of the group according to the conditions we want, just replace Student::getScore with the conditions we want, such as: 

    Map<String, List<Integer>> map3 = stream.collect(Collectors.groupingBy(stu -> {
                                                                                    if (stu.getScore() > 60) {
                                                                                        return "PASS";
                                                                                    } else {
                                                                                        return "FAIL";
                                                                                    }
                                                                                }, Collectors.mapping(Student::getId, Collectors.toList())));

According to this idea, we can freely process the elements in the stream into the result data we want.

Guess you like

Origin blog.csdn.net/zhoushimiao1990/article/details/128080919