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 super T, ? extends U> 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
以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 super T> 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类中的方法很有帮助
以上,如有不对请指正