一、概述
1、简介
今天我们来彻底的从头到尾的一个一个API的对Collectors进行讲解。
2、注意
以实战为主,因为前面几篇文章讲解的实在太透彻,而且Collectors只是工具类,我们能够知道每个api什么意思,怎么玩,什么效果就行了。
二、collect()源码
java.util.stream.Stream <R, A> R collect(Collector<? super T, A, R> collector);
我们看到他接收一个Collector类为参数。但是恰巧java8的时候开发者给我们内置了一个Collectors类来更方便的操作Collector,集成了很多让开发者方便的API。
三、API
API很多,源码我们不看,没那必要,想看的自己看,但是到最后我会讲解一下Collector的工作原理。这里我写Demo也好,看含义也好都是参照的jdk8文档来的,所以直接上图了。下面我会针对每个API都做一个demo,加深印象。
四、案例
需求很简单,就是将苹果按照颜色分类,最终返回Map<String, List<Apple>>格式的数据
@Data @AllArgsConstructor public class Apple { /** 颜色 */ private String color; /** 重量 */ private long weight; } package com.chentongwei.java8.collector; import com.chentongwei.java8.lambda.Apple; import java.util.*; import java.util.stream.Collectors; /** * @author chentongwei@baidu-mgame.com 2018-12-17 11:42:12 * @Desc */ public class CollectorIntroduce { public static void main(String[] args) { List<Apple> list = Arrays.asList(new Apple("green", 150) , new Apple("yellow", 120) , new Apple("green", 170) , new Apple("green", 150) , new Apple("yellow", 120) , new Apple("green", 170)); /* * 常规方式 */ Optional.ofNullable(groupByNormal(list)).ifPresent(System.out::println); System.out.println("==================================================="); /* * lambda方式 */ Optional.ofNullable(groupByFunction(list)).ifPresent(System.out::println); System.out.println("==================================================="); /* * Collector上场 */ Optional.ofNullable(groupByCollector(list)).ifPresent(System.out::println); } /** * 常规方式 */ private static Map<String, List<Apple>>groupByNormal(List<Apple> apples) { Map<String, List<Apple>> map = new HashMap<>(); for (Apple a : apples) { List<Apple> list = map.get(a.getColor()); if (null == list) { list = new ArrayList<>(); map.put(a.getColor(), list); } list.add(a); } return map; } /** * lambda方式 */ private static Map<String, List<Apple>>groupByFunction(List<Apple> apples) { Map<String, List<Apple>> map = new HashMap<>(); apples.parallelStream().forEach(a -> { List<Apple> colorList =Optional.ofNullable(map.get(a.getColor())).orElseGet(() -> { List<Apple> list = new ArrayList<>(); map.put(a.getColor(), list); return list; }); colorList.add(a); }); return map; } /** * 强大的Collector方式 */ private static Map<String, List<Apple>>groupByCollector(List<Apple> apples) { returnapples.parallelStream().collect(Collectors.groupingBy(Apple::getColor)); } }
五、ApiDemo
/** * @author chentongwei@baidu-mgame.com 2018-12-10 12:11:29 * @Desc */ @Data @AllArgsConstructor @NoArgsConstructor public class Dish { private String name; private boolean vegetarian; private int calories; private Type type; public enum Type {MEAT, FISH, OTHER} } package com.chentongwei.java8.collector; import com.chentongwei.java8.stream.Dish; import java.util.*; import java.util.stream.Collectors; /** * @author chentongwei@baidu-mgame.com 2018-12-17 12:13:24 * @Desc */ public class CollectorsAction { public static final List<Dish> menu = Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), new Dish("beef", false, 700, Dish.Type.MEAT), new Dish("chicken", false, 400, Dish.Type.MEAT), new Dish("french fries", true, 530, Dish.Type.OTHER), new Dish("rice", true, 350, Dish.Type.OTHER), new Dish("season fruit", true, 120, Dish.Type.OTHER), new Dish("pizza", true, 550, Dish.Type.OTHER), new Dish("prawns", false, 300, Dish.Type.FISH), new Dish("salmon", false, 450, Dish.Type.FISH)); public static void main(String[] args) { testAveragingDouble(); testAveragingInt(); testAveragingLong(); testCollectiongAndThen(); testCounting(); testGroupByFunction(); testGroupByFunctionAndCollector(); testGroupingByFunctionAndSupplierAndCollector(); } /** * 求平均值,double类型,getCalories()是int类型,int隐式转换double */ private static void testAveragingDouble() { System.out.println("**testAveragingDouble**"); Optional.ofNullable( menu.stream().collect(Collectors.averagingDouble(Dish::getCalories)) ).ifPresent(System.out::println); // 466.6666666666667 } /** * 求平均值,int类型 */ private static void testAveragingInt() { System.out.println("**testAveragingInt**"); Optional.ofNullable( menu.stream().collect(Collectors.averagingInt(Dish::getCalories)) ).ifPresent(System.out::println); // 466.6666666666667 } /** * 求平均值,long类型,隐式转换 */ private static void testAveragingLong() { System.out.println("**testAveragingLong**"); Optional.ofNullable( menu.stream().collect(Collectors.averagingLong(Dish::getCalories)) ).ifPresent(System.out::println); // 466.6666666666667 } /** * 两个参数: * 1、拿到自己想要的结果。 * 2、针对1的结果进行二次处理 */ private static void testCollectiongAndThen() { System.out.println("**testCollectiongAndThen**"); Optional.ofNullable(menu.stream().collect( Collectors.collectingAndThen( Collectors.averagingInt(Dish::getCalories), a -> "the average calories is -> " + a) ) ) // the average calories is -> 466.6666666666667 .ifPresent(System.out::println); } /** * 统计总数 */ private static void testCounting() { System.out.println("**testCounting**"); Optional.ofNullable( menu.stream().collect(Collectors.counting()) ).ifPresent(System.out::println); // 9 } /** * 按照type分组 */ private static void testGroupByFunction() { System.out.println("**testGroupByFunction**"); Optional.ofNullable( menu.stream().collect(Collectors.groupingBy(Dish::getType)) ).ifPresent(System.out::println); } /** * 按照type分组,且输出每个组内有多少个 * 返回的是map类型 */ private static void testGroupByFunctionAndCollector() { System.out.println("**testGroupByFunctionAndCollector**"); Optional.ofNullable( // 返回的是map menu.stream().collect(Collectors.groupingBy(Dish::getType,Collectors.counting())) ).ifPresent(System.out::println); // {OTHER=4, MEAT=3, FISH=2} } /** * 按照type分组,且输出每个组内有多少个 * 返回自定义map类型 */ private static voidtestGroupingByFunctionAndSupplierAndCollector() { System.out.println("**testGroupingByFunctionAndSupplierAndCollector**"); // 返回自定义类型,这里是treemap,也可以是其他包下的map只要实现了map接口就行(并不一定都要是jdk自带的才行) Map<Dish.Type, Long> collect =menu.stream().collect(Collectors.groupingBy(Dish::getType,TreeMap::new, Collectors.counting())); // class java.util.TreeMap Optional.ofNullable(collect.getClass()).ifPresent(System.out::println); //{MEAT=3, FISH=2, OTHER=4} Optional.ofNullable(collect).ifPresent(System.out::println); } } package com.chentongwei.java8.collector; import com.chentongwei.java8.stream.Dish; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.stream.Collectors; /** * @author chentongwei@baidu-mgame.com 2018-12-18 18:08:01 * @Desc */ public class CollectorsAction2 { private static final List<Dish> menu = CollectorsAction.menu; public static void main(String[] args) { testGroupingByConcurrent(); testGroupingByConcurrentWithFunctionAndCollector(); testGroupingByConcurrentWithFunctionAndSupplierAndCollector(); testJoining(); testJoiningWithDelimiter(); testJoiningWithDelimiterAndPrefixAndSuffix(); testMapping(); testMaxBy(); testMinBy(); } /** * 与groupingBy一样的,只是返回ConcurrentHashMap,线程安全的map */ private static void testGroupingByConcurrent() { } private static voidtestGroupingByConcurrentWithFunctionAndCollector() { } private static voidtestGroupingByConcurrentWithFunctionAndSupplierAndCollector() { } /** * 拼接字符串 */ private static void testJoining() { System.out.println("***testJoining***"); String collect = menu.stream().map(dish ->dish.getName()).collect(Collectors.joining()); // porkbeefchickenfrench friesriceseason fruitpizzaprawnssalmon Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 以某种自定义分隔符拼接字符串 */ private static void testJoiningWithDelimiter() { System.out.println("***testJoiningWithDelimiter***"); String collect = menu.stream().map(dish ->dish.getName()).collect(Collectors.joining("|")); // pork|beef|chicken|french fries|rice|season fruit|pizza|prawns|salmon Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 以某种自定义分隔符拼接字符串且在头和尾部都拼上前后缀 */ private static void testJoiningWithDelimiterAndPrefixAndSuffix() { System.out.println("***testJoiningWithDelimiter***"); String collect = menu.stream().map(dish ->dish.getName()).collect(Collectors.joining("|", "result=[", "]")); // result=[pork|beef|chicken|french fries|rice|season fruit|pizza|prawns|salmon] Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 效果等同于joining,只是joining需要提前map找到字符,而mapping则不需要,因为mapping两个参数,第一个是找到字符,第二个joining的操作 * 当然也不只是可以joining,我只是举例。 */ private static void testMapping() { System.out.println("***testMapping***"); String collect =menu.stream().collect(Collectors.mapping(Dish::getName,Collectors.joining("|"))); // pork|beef|chicken|french fries|rice|season fruit|pizza|prawns|salmon Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 取最大值 */ private static void testMaxBy() { System.out.println("***testMaxBy***"); Optional<Dish> collect =menu.stream().collect(Collectors.maxBy(Comparator.comparingInt(Dish::getCalories))); // Dish(name=pork, vegetarian=false, calories=800, type=MEAT) collect.ifPresent(System.out::println); } /** * 取最小值 */ private static void testMinBy() { System.out.println("***testMinBy***"); Optional<Dish> collect =menu.stream().collect(Collectors.minBy(Comparator.comparingInt(Dish::getCalories))); // Dish(name=season fruit, vegetarian=true, calories=120, type=OTHER) collect.ifPresent(System.out::println); } } package com.chentongwei.java8.collector; import com.chentongwei.java8.stream.Dish; import java.util.*; import java.util.function.BinaryOperator; import java.util.stream.Collectors; /** * @author chentongwei@baidu-mgame.com 2018-12-19 12:04:51 * @Desc */ public class CollectorsAction3 { private static final List<Dish> menu = CollectorsAction.menu; public static void main(String[] args) { testPartitioningByWithPredicate(); testPartitioningByWithPredicateAndCollector(); testReducingBinaryOperator(); testReducingBinaryOperatorAndIdentity(); testReducingBinaryOperatorAndIdentiyAndFunction(); testSummarizingDouble(); testSummarizingLong(); testSummarizingInt(); } /** * 按照是否是水果进行分类 */ private static void testPartitioningByWithPredicate() { System.out.println("**testPartitioningByWithPredicate**"); Map<Boolean, List<Dish>> collect =menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian)); Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 按照是否是水果进行分类 * 且获取分类下的平均值 */ private static void testPartitioningByWithPredicateAndCollector() { System.out.println("**testPartitioningByWithPredicate**"); Map<Boolean, Double> collect =menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian,Collectors.averagingDouble(Dish::getCalories))); Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 取出Calories最大的 */ private static void testReducingBinaryOperator() { System.out.println("**testReducingBinaryOperator**"); Optional<Dish> collect = menu.stream().collect( Collectors.reducing(BinaryOperator.maxBy(Comparator.comparingInt(Dish::getCalories))) ); Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 取出Calories总和,且加上10 * 这就是stream篇幅讲的reduce,很熟悉了已经。 */ private static void testReducingBinaryOperatorAndIdentity() { System.out.println("**testReducingBinaryOperatorAndIdentity**"); Integer collect = menu.stream().map(dish ->dish.getCalories()).collect(Collectors.reducing(10, (d1, d2) -> d1 +d2)); Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 和上面的一样,只是不用map了,当参数传进去 */ private static voidtestReducingBinaryOperatorAndIdentiyAndFunction() { System.out.println("**testReducingBinaryOperatorAndIdentiyAndFunction**"); Integer result = menu.stream().collect(Collectors.reducing(0,Dish::getCalories, (d1, d2) -> d1 + d2)); System.out.println(result); } /** * 下面三组: * 里面包含了最大值、最小值、平均值。 * 所以想要单个的话直接在最后.就行了,这些方法都包含在如:IntSummaryStatistics里 */ private static void testSummarizingDouble() { System.out.println("**testSummarizingDouble**"); IntSummaryStatistics collect =menu.stream().collect(Collectors.summarizingInt(Dish::getCalories)); Optional.ofNullable(collect).ifPresent(System.out::println); } private static void testSummarizingLong() { System.out.println("**testSummarizingLong**"); LongSummaryStatistics collect =menu.stream().collect(Collectors.summarizingLong(Dish::getCalories)); Optional.ofNullable(collect).ifPresent(System.out::println); } private static void testSummarizingInt() { System.out.println("**testSummarizingInt**"); IntSummaryStatistics collect =menu.stream().collect(Collectors.summarizingInt(Dish::getCalories)); Optional.ofNullable(collect).ifPresent(System.out::println); } /** * 余下的还有N组toXXX,比如:toList、toSet等等,这些就自己尝试吧,就是将结果集转成某种类型。 */ }
六、阐述API原理
1、举例
// 1 Collectors.groupingBy(Dish::getType) // 2 Collectors.groupingBy(Dish::getType, Collectors.counting())
2、补充
如果你问:什么鬼?写法看不懂,那我只能说你如下:
- 没看入参类型?
- 前面讲的函数式接口等基础核心知识没学会?
3、讲解
3.1
Collectors.groupingBy(Dish::getType)
1.看源码
groupingBy(Function<? super T, ? extends K> classifier) { return groupingBy(classifier, toList()); }
2.分析好的,我们看到了入参是Function函数式接口,而此接口是一入参一返回的。所以我们可以写成a -> a.getType(),入参a,返回a.getType();利用函数推到写成Dish::getType。
3.2
Collectors.groupingBy(Dish::getType, Collectors.counting())
1.看源码
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ?extends K> classifier, Collector<? super T, A, D> downstream) { return groupingBy(classifier, HashMap::new, downstream); }
2.分析与上面那个一样,只是多了一个参数Collector类型的,且泛型是T,A,D,恰恰T是第一个参数的泛型(入参),所以....不多bb,懂了吧。就是针对某个参数进行Function操作然后在进行自定义处理的Collector操作。