Java8--Stream的各种用法(二):collect、Collectors

Collectors中的方法: 

 其中我们常用的是前三个:将流中的元素放到集合中、分组、toMap。 下面我们逐个介绍这些方法的使用.

基础类: Student

public class Student {
    private Integer id;

    private String name;

    private String className;

    private Double score;
 
    private Long timeStamp;
}

1、将流中的元素放到集合中: 

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);
    }

示例: 

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

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

1.2  toList() 、toSet() :

直接上示例: 

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

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

2、分组

2.1 groupingBy 它有三个重载方法: 

    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) {
        ......
    }

第一个方法只需一个分组参数classifier,内部自动将结果保存到一个map中,每个map的键为 ‘?’ 类型(即classifier的结果类型),值为一个list,这个list中保存在属于这个组的元素。 但是它实际是调用了第二个方法-- Collector 默认为list。而第二个方法实际是调用第三个方法,默认Map的生成方式为HashMap。 第三个方法才是真实完整的分组逻辑处理。

 示例: 

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  

返回一个并发Collector收集器对T类型的输入元素执行"group by"操作, 也有三个重载的方法, 其使用与groupingBy 基本相同。 

2.3 partitioningBy

该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的集合

两个重载的方法: 

    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) {
        ......
    }

从上面的重载方法中可以看出,partitioningBy 与 groupingBy 类似, 只不过partitioningBy 生成的map的key的类型限制只能是Boolean类型。

示例: 

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

toMap方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的map。

toMap也有三个重载的方法: 

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){
    ......
}

三个重载的方法,最终都是调用第三个方法来实现, 第一个方法中默认指定了key重复的处理方式和map的生成方式; 而第二个方法默认指定了map的生成方式,用户可以自定义key重复的处理方式。

示例: 

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));
toConcurrentMap的使用与toMap基本一致, 只不过toConcurrentMap 用于处理并发请求,它生成的map是 ConcurrentHashMap

4、元素拼接joining

三个重载方法: 

  1. joining() : 没有分隔符和前后缀,直接拼接
  2. joining(CharSequence delimiter) : 指定元素间的分隔符
  3. joining(CharSequence delimiter,CharSequence prefix, CharSequence suffix):  指定分隔符和整个字符串的前后缀。
Stream<String> stream = Stream.of("1", "2", "3", "4", "5", "6");
String s = stream.collect(Collectors.joining(",", "prefix", "suffix"));
        

joining 之前,Stream 必须是Stream<String> 类型

5、类型转换

mapping:这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳。 类似与Stream的map方法。

collectingAndThen:在归纳动作结束之后,对归纳的结果进行再处理。

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、聚合

  1. counting: 同 stream.count()
  2. minBy:  同stream.min()
  3. maxBy: 同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

reducing方法有三个重载方法,其实是和Stream里的三个reduce方法对应的,二者是可以替换使用的,作用完全一致,也是对流中的元素做统计归纳作用。

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) {
    ......
}

示例:

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));

扩展: 

实际运用中,可能会用到比较复杂的 groupingBy、mapping、toMap 嵌套、组合使用,进行多级分组处理数据。如: 

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)));

当然也可以根据我们想要的条件,设置分组的组合条件,只需要替换 Student::getScore ,换成我们想要的条件即可, 如: 

    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())));

按照这种思路,我们可以随意处理stream中的元素成我们想要的结果数据。

猜你喜欢

转载自blog.csdn.net/zhoushimiao1990/article/details/128080919
今日推荐