Collectors类浅析

toList

收集成ArrayList

List list = people.stream().map(Person::getName).collect(Collectors.toList());

toSet

收集成HashSet

List list = people.stream().map(Person::getName).collect(Collectors.toSet());

toMap、toConcurrentMap

收集成map

Map map = list.stream().collect(Collectors.toMap(Member::getId, Member::getImgPath));

ps:
value不能为空,否贼抛出异常,因为底层使用map的merge,该方法不允许为空
key不允许重复,否贼抛出异常(据说jdk9中改了)
stream提供了collect的重载方法(针对类似上述的情况,都可自定义收集方案)

Map memberMap = list.stream().collect(HashMap::new, (m,v)->
    m.put(v.getId(), v.getImgPath()),HashMap::putAll);

supplier为新的容器 accumulator为计算方案 combiner为结合方案 m为容器 v为元素
combiner 展开即 (x,y)->x.addAll(y) 其中 x指向HashMap::new y指向m(个人理解,不对别打我)

toCollection

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

无论toList 或toSet 本质上都是对toCollection的细化,即将collectionFactory变为具体的容器类型,比如ArrayList::new和HashSet::new,toList、toSet可以看作是语法糖,而toCollection是未指定容器类型的方案

LinkedHashSet collect = list.stream().collect(Collectors.toCollection(LinkedHashSet::new));

join

类似StringUtils.join

public static Collector joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix) {
        return new CollectorImpl<>(
                () -> new StringJoiner(delimiter, prefix, suffix),
                StringJoiner::add, StringJoiner::merge,
                StringJoiner::toString, CH_NOID);
    }

delimiter 分隔符 prefix前缀 suffix后缀

List list2 = Arrays.asList("1", "2", "3");
String collect1 = list2.stream().collect(Collectors.joining());
String collect2 = list2.stream().collect(Collectors.joining(";"));
String collect3 = list2.stream().collect(Collectors.joining(";","<<<",">>>"));

结果:

"123"
"1;2;3"
"<<<1;2;3>>>"

counting

计数

Long collect1 = list2.stream().collect(Collectors.counting());
long collect2 = list2.stream().count();

两者等效,counting是reducing的细化

counting源码:

public static  Collector
counting() {
    return reducing(0L, e -> 1L, Long::sum);
}

minby,maxby

最小值最大值

BmGroupEntity collect1 = list.stream().collect(Collectors.minBy(Comparator.comparing(BmGroupEntity::getId))).orElse(null);
BmGroupEntity collect1 = list.stream().min(Comparator.comparing(BmGroupEntity::getId)).orElse(null);

等效,底层都是 BinaryOperator.minBy

summing系 averaging系 summarizing系

求和,求平均数 ,求信息汇总等

groupingBy

分组

Map> collect1 = list.stream().collect(Collectors.groupingBy(BmGroupEntity::getId));

支持二级分组

Map>> collect1 = list.stream().collect(Collectors.groupingBy(BmGroupEntity::getId, Collectors.groupingBy(BmGroupEntity::getTableName)));

还有个groupingByConcurrent,以ConcurrentMap为容器

partitioningBy

也是分组,但只分成两组,true一组,false一组,同样也支持二级分组

Map> jigeUsers = userList.stream().collect(Collectors.partitioningBy(user -> user.getScore() >= 60));

reducing

自定义归约

Long collect = list.stream().collect(Collectors.counting());//code1

转为reducing即:

Integer collect2 = list.stream().collect(Collectors.reducing(0, e -> 1L, (x, y) -> x + y));//code2

reducing第一个参数为初始值,第二个为元素中需要归约计算的属性,第三个归约方案
当集合的元素为数值类型时,可以用单参的reduce,即只要第三个参数,其余两个使用默认值,初始值为0,参与计算的对象为集合中的元素
x和y并不是指几何中相邻的两个元素,y是指向e-1L的计算结果,x指的是初始值,在上述代码中为0

源码:

public static 
    Collector reducing(U identity,
                                Function mapper,
                                BinaryOperator op) {
        return new CollectorImpl<>(
                boxSupplier(identity),
                (a, t) -> { a[0] = op.apply(a[0], mapper.apply(t)); },
                (a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
                a -> a[0], CH_NOID);
    }

boxSupplier是将默认值变为数组,因此a是个数组。
CollectorImpl是在Colletcs中实现了Collect接口的内部类

补充:

CollectorImpl

CollectorImpl的构造器:
CollectorImpl(Supplier supplier, BiConsumer accumulator, BinaryOperator combiner, Function finisher, Set characteristics)

以counting为例,将code2代入进去看看也许更清晰

Supplier supplier = Object[] a;
//容器提供者为a
BiConsumer accumulator = (a, t) -> { a[0] = a[0] + ((t)->1L)); },
//accumulator是op和mapper结合的产物,但这里的a并不指向实际的容器a,而是指向一个空的新容器且和a完全同类型,这里计算出的结果全都会存入这个新的a容器,即combiner中的b
BinaryOperator combiner = (a, b) -> { a[0] = a[0] + b[0]; return a; },
//这里的a指向supplier中的容器a,b指向accumulator中的容器a
Function finisher = a -> a[0];
//以数组中的第一个值为结果返回
Set characteristics = CH_NOID
//这个还没研究

再用averagingInt做例子看看:

List list3 = Arrays.asList(1,2,3);
Double collect2 = list3.stream().collect(Collectors.averagingInt(x -> x));

代入以下源码

public static  Collector averagingInt(ToIntFunction mapper) {
        return new CollectorImpl<>(
                () -> new long[2],
                (a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; },
                (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
                a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);
    }

supplier:原始容器 new long[2],我们称之为a
accumulator: (a, t) -> { a[0] += t; a[1]++; } //这里a是虚拟容器临时容器,它不是supplier的a,但和supplier的a一毛一样
combiner : (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; }//这里的a指向supplier的a,b指向accumulator的a
finisher: a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1]
//a[0]存放的是元素的总值,a[1]存放是是元素个数,因此返回结果是(double) a[0] / a[1]

理解CollectorImpl的构造对于理解Collects类中的方法很有帮助
以上,如有不对请指正

你可能感兴趣的:(Collectors类浅析)