1. 多个分组
Map<Long,Map<Long,List<RStudentExam>>> map = list.stream().collect(Collectors.groupingBy(RStudentExam::getId,Collectors.groupingBy(RStudentExam::getSchoolId)));
2. 分组后求和
参考链接:
https://blog.csdn.net/weixin_44905182/article/details/105792291
需求:
求得每个学生的总成绩
模拟数据如下:
List<StudentScore> studentScoreList = new ArrayList<>();StudentScore studentScore1 = new StudentScore("慕容皝","语文",BigDecimal.valueOf(99));StudentScore studentScore2 = new StudentScore("慕容皝","数学",BigDecimal.valueOf(99));StudentScore studentScore3 = new StudentScore("慕容皝","英语",BigDecimal.valueOf(97));StudentScore studentScore4 = new StudentScore("慕容皝","历史",BigDecimal.valueOf(97));studentScoreList.add(studentScore1) ;studentScoreList.add(studentScore2) ;studentScoreList.add(studentScore3) ;studentScoreList.add(studentScore4) ;StudentScore studentScore5 = new StudentScore("慕容垂","语文",BigDecimal.valueOf(89));StudentScore studentScore6 = new StudentScore("慕容垂","数学",BigDecimal.valueOf(89));StudentScore studentScore7 = new StudentScore("慕容垂","英语",BigDecimal.valueOf(87));StudentScore studentScore8 = new StudentScore("慕容垂","历史",BigDecimal.valueOf(87));studentScoreList.add(studentScore5) ;studentScoreList.add(studentScore6) ;studentScoreList.add(studentScore7) ;studentScoreList.add(studentScore8) ;StudentScore studentScore9 = new StudentScore("慕容雪","语文",BigDecimal.valueOf(79));StudentScore studentScore10 = new StudentScore("慕容雪","数学",BigDecimal.valueOf(79));StudentScore studentScore11 = new StudentScore("慕容雪","英语",BigDecimal.valueOf(77));StudentScore studentScore12 = new StudentScore("慕容雪","历史",BigDecimal.valueOf(77));studentScoreList.add(studentScore9) ;studentScoreList.add(studentScore10) ;studentScoreList.add(studentScore11) ;studentScoreList.add(studentScore12) ;
常规做法:
map 的merge 方法
java8 分组后对组内数据的处理(扩展)
常规:
// 法① 常规做法Map<String, BigDecimal> studentScoreMap1 = new HashMap<>();studentScoreList.forEach(studentScore -> {if (studentScoreMap1.containsKey(studentScore.getName())) {studentScoreMap1.put(studentScore.getName(),// 拿到旧值,在原来的基础上操作studentScoreMap1.get(studentScore.getName()).add(studentScore.getScore()));} else {studentScoreMap1.put(studentScore.getName(), studentScore.getScore());}});studentScoreMap1.forEach((k,v) -> System.out.println("key: " + k + " , " + "value: " + v));System.out.println("-----------------------------sum----------------------");
merge:
// 法②Map<String, BigDecimal> studentScoreMap2 = new HashMap<>();studentScoreList.forEach(studentScore -> studentScoreMap2.merge(// keystudentScore.getName(),// valuestudentScore.getScore(),(a,b) ->{// add 是返回一个新的 BigDecimal 对象a = a.add(b);return a;}));studentScoreMap2.forEach((k,v) -> System.out.println("key: " + k + " , " + "value: " + v));
merge方法定义在 java.util.Map 中,方法定义如下:
default V merge(K key, V value,BiFunction<? super V, ? super V, ? extends V> remappingFunction) {Objects.requireNonNull(remappingFunction);Objects.requireNonNull(value);// 获取旧值V oldValue = get(key);// 旧值为null ,用value 直接作为新value ; 旧值非null ,执行 函数接口V newValue = (oldValue == null) ? value :remappingFunction.apply(oldValue, value);if(newValue == null) {remove(key);} else {// insert or updateput(key, newValue);}return newValue;}
BiFunction 定义:
@FunctionalInterface
public interface BiFunction<T, U, R> {/*** Applies this function to the given arguments* @param t the first function argument* @param u the second function argument* @return the function result*/R apply(T t, U u);
}
法③
需要自定义 函数式接口 etc(参考int 的算法,比较麻烦)
// 法③ Java8原生只提供了summingInt、summingLong、summingDouble三种基础类型的方法,想要对BigDecimal类型的数据操作需要自己新建工具类System.out.println("-----------------------------sum 3.1 ----------------------");Map<String,BigDecimalSummaryStatistics> studentScoreMap4 =studentScoreList.stream().collect(Collectors.groupingBy(StudentScore::getName , CollectorsUtil.summarizingBigDecimal(StudentScore::getScore)) );studentScoreMap4.forEach((k,v) -> System.out.println("key: " + k + " , " + "value: " + v.getSum()));
这个最常用:
public static <T, K> Collector<T, ?, Map<K, List<T>>>groupingBy(Function<? super T, ? extends K> classifier) {return groupingBy(classifier, toList());}
上面的用到的底层是下面的
public static <T, K, A, D>Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream) {return groupingBy(classifier, HashMap::new, downstream);}
而上面两个方法的最底层调用的都是 下面的:
public static <T, K, D, A, M extends Map<K, D>>Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,Supplier<M> mapFactory,Collector<? super T, A, D> downstream) {Supplier<A> downstreamSupplier = downstream.supplier();BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());downstreamAccumulator.accept(container, t);};BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner());@SuppressWarnings("unchecked")Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);}else {@SuppressWarnings("unchecked")Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();Function<Map<K, A>, M> finisher = intermediate -> {intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));@SuppressWarnings("unchecked")M castResult = (M) intermediate;return castResult;};return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);}}
对分组后,组内数据是int 的求和
Foo foo1 = new Foo(1, 2);Foo foo2 = new Foo(2, 23);Foo foo3 = new Foo(2, 6);List<Foo> list = new ArrayList<>(4);list.add(foo1);list.add(foo2);list.add(foo3);Map<Integer, IntSummaryStatistics> collect = list.stream().collect(Collectors.groupingBy(Foo::getCode, Collectors.summarizingInt(Foo::getCount)));IntSummaryStatistics statistics1 = collect.get(1);System.out.println(statistics1.getSum());System.out.println(statistics1.getAverage());System.out.println(statistics1.getMax());System.out.println(statistics1.getMin());System.out.println(statistics1.getCount());
源码如下:
public static <T>Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) {return new CollectorImpl<T, IntSummaryStatistics, IntSummaryStatistics>(IntSummaryStatistics::new,(r, t) -> r.accept(mapper.applyAsInt(t)),(l, r) -> { l.combine(r); return l; }, CH_ID);}
构造器定义如下:
static class CollectorImpl<T, A, R> implements Collector<T, A, R> {private final Supplier<A> supplier;private final BiConsumer<A, T> accumulator;private final BinaryOperator<A> combiner;private final Function<A, R> finisher;private final Set<Characteristics> characteristics;CollectorImpl(Supplier<A> supplier,BiConsumer<A, T> accumulator,BinaryOperator<A> combiner,Function<A,R> finisher,Set<Characteristics> characteristics) {this.supplier = supplier;this.accumulator = accumulator;this.combiner = combiner;this.finisher = finisher;this.characteristics = characteristics;}CollectorImpl(Supplier<A> supplier,BiConsumer<A, T> accumulator,BinaryOperator<A> combiner,Set<Characteristics> characteristics) {this(supplier, accumulator, combiner, castingIdentity(), characteristics);}
package java.util;import java.util.function.IntConsumer;
import java.util.stream.Collector;/*** @since 1.8*/
public class IntSummaryStatistics implements IntConsumer {private long count;private long sum;private int min = Integer.MAX_VALUE;private int max = Integer.MIN_VALUE;public IntSummaryStatistics() { }@Overridepublic void accept(int value) {// 计数,求平均数用的++count;// 求和sum += value;// 最小值min = Math.min(min, value);// 最大值max = Math.max(max, value);}// 计算对应的值public void combine(IntSummaryStatistics other) {count += other.count;sum += other.sum;min = Math.min(min, other.min);max = Math.max(max, other.max);}public final long getCount() {return count;}// 返回和 public final long getSum() {return sum;}// 返回最小值public final int getMin() {return min;}// 返回最大值public final int getMax() {return max;}// 返回平均值(sum 除以 count)public final double getAverage() {return getCount() > 0 ? (double) getSum() / getCount() : 0.0d;}@Overridepublic String toString() {return String.format("%s{count=%d, sum=%d, min=%d, average=%f, max=%d}",this.getClass().getSimpleName(),getCount(),getSum(),getMin(),getAverage(),getMax());}
}
而他的父接口是函数式接口
package java.util.function;import java.util.Objects;/*** @since 1.8*/
@FunctionalInterface
public interface IntConsumer {void accept(int value);}
@FunctionalInterface
public interface BiConsumer<T, U> {void accept(T t, U u);
}
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T>{}
@FunctionalInterface
public interface BiFunction<T, U, R> {R apply(T t, U u);
}
自定义函数式接口
@FunctionalInterface
public interface ToIntFunction<T> {int applyAsInt(T value);
}
接下来改写 一个针对BigDecimal 类型的 group by
先定义初始化对象 ,返回对象
package com.example.demo.service.Impl;import com.example.demo.entity.annotation.BigDecimalConsumer;import java.math.BigDecimal;/*** @author: guoyiguang**/
public class BigDecimalSummaryStatistics implements BigDecimalConsumer {private BigDecimal sum;public BigDecimalSummaryStatistics() { }public BigDecimal combine(BigDecimalSummaryStatistics other) {// 求和return sum = sum.add(other.sum) ;}@Overridepublic void accept(BigDecimal value) {if (null == sum){sum = value ;}else {// 计算sum = sum.add(value);}}@Overridepublic String toString() {return "BigDecimalSummaryStatistics{" +"sum=" + sum +'}';}// 要获取到这个对象里的 sum 需要get 方法public BigDecimal getSum() {return sum;}
}
自定义函数式接口
@FunctionalInterface
public interface ToBigDecimalFunction<T> {// 传一个 ,返回 BigDecimal 对象BigDecimal applyAsBigDecimal(T value);
}
提供一个返回 stream 流 里的 Collector 的工具类
package com.example.demo.utils;import com.example.demo.entity.annotation.ToBigDecimalFunction;
import com.example.demo.service.Impl.BigDecimalSummaryStatistics;
import java.util.Collections;
import java.util.Set;
import java.util.function.*;
import java.util.stream.Collector;public class CollectorsUtil {static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();private CollectorsUtil() {}@SuppressWarnings("unchecked")private static <I, R> Function<I, R> castingIdentity() {return i -> (R) i;}static class CollectorImpl<T, A, R> implements Collector<T, A, R> {private final Supplier<A> supplier;private final BiConsumer<A, T> accumulator;private final BinaryOperator<A> combiner;private final Function<A, R> finisher;private final Set<Characteristics> characteristics;CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,Function<A, R> finisher, Set<Characteristics> characteristics) {this.supplier = supplier;this.accumulator = accumulator;this.combiner = combiner;this.finisher = finisher;this.characteristics = characteristics;}// 模仿int写的CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,Set<Characteristics> characteristics) {this(supplier, accumulator, combiner, castingIdentity(), characteristics);}@Overridepublic BiConsumer<A, T> accumulator() {return accumulator;}@Overridepublic Supplier<A> supplier() {return supplier;}@Overridepublic BinaryOperator<A> combiner() {return combiner;}@Overridepublic Function<A, R> finisher() {return finisher;}@Overridepublic Set<Characteristics> characteristics() {return characteristics;}}// CollectorImpl(Supplier<A> supplier,// BiConsumer<A, T> accumulator,// BinaryOperator<A> combiner,// Set<Characteristics> characteristics)
// T 不管
// 第一个 BigDecimalSummaryStatistics 是初始化对象
// 第二个 BigDecimalSummaryStatistics 是返回对象public static <T>Collector<T, BigDecimalSummaryStatistics, BigDecimalSummaryStatistics> summarizingBigDecimal(ToBigDecimalFunction<? super T> mapper) {
// T 不管
// 第一个 BigDecimalSummaryStatistics 是初始化对象
// 第二个 BigDecimalSummaryStatistics 是返回对象return new CollectorImpl<T, BigDecimalSummaryStatistics, BigDecimalSummaryStatistics>(BigDecimalSummaryStatistics::new ,// 上一行 new 出来的 BigDecimalSummaryStatistics 对象作为 BiConsumer 的第一个参数(r, t) -> r.accept(mapper.applyAsBigDecimal(t)),// 上一行 new 出来的 BigDecimalSummaryStatistics 对象作为 BinaryOperator 的第二个参数(l, r) -> { l.combine(r); return l; }, CH_NOID);}}
测试代码如下:
// 法③ Java8原生只提供了summingInt、summingLong、summingDouble三种基础类型的方法,想要对BigDecimal类型的数据操作需要自己新建工具类System.out.println("-----------------------------sum 3.1 ----------------------");Map<String,BigDecimalSummaryStatistics> studentScoreMap4 =studentScoreList.stream().collect(Collectors.groupingBy(StudentScore::getName , CollectorsUtil.summarizingBigDecimal(StudentScore::getScore)) );studentScoreMap4.forEach((k,v) -> System.out.println("key: " + k + " , " + "value: " + v.getSum()));
测试结果:
-----------------------------sum 3.1 ----------------------
key: 慕容皝 , value: 392
key: 慕容垂 , value: 352
key: 慕容雪 , value: 312