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(
// key
studentScore.getName(),
// value
studentScore.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 update
put(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() { }
@Override
public 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;
}
@Override
public 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) ;
}
@Override
public void accept(BigDecimal value) {
if (null == sum){
sum = value ;
}else {
// 计算
sum = sum.add(value);
}
}
@Override
public 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);
}
@Override
public BiConsumer<A, T> accumulator() {
return accumulator;
}
@Override
public Supplier<A> supplier() {
return supplier;
}
@Override
public BinaryOperator<A> combiner() {
return combiner;
}
@Override
public Function<A, R> finisher() {
return finisher;
}
@Override
public 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