java?stream實現(xiàn)分組BigDecimal求和以及自定義分組求和
前言
隨著微服務(wù)的發(fā)展,越來越多的sql處理被放到j(luò)ava來處理,數(shù)據(jù)庫經(jīng)常會使用到對集合中的數(shù)據(jù)進(jìn)行分組求和,分組運算等等。
那怎么樣使用java的stream優(yōu)雅的進(jìn)行分組求和或運算呢?
一、準(zhǔn)備測試數(shù)據(jù)
這里測試數(shù)據(jù)學(xué)生,年齡類型是Integer,身高類型是BigDecimal,我們分別對身高個年齡進(jìn)行求和。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
/**
* 姓名
*/
private String name;
/**
* 年齡
*/
private Integer age;
/**
* 身高
*/
private BigDecimal stature;
}
public class LambdaLearn {
// 初始化的測試數(shù)據(jù)集合
static List<Student> list = new ArrayList<>();
static {
// 初始化測試數(shù)據(jù)
list.add(new Student("張三", 18, new BigDecimal("185")));
list.add(new Student("張三", 19, new BigDecimal("185")));
list.add(new Student("張三2", 20, new BigDecimal("180")));
list.add(new Student("張三3", 20, new BigDecimal("170")));
list.add(new Student("張三3", 21, new BigDecimal("172")));
}
}
二、按學(xué)生姓名分組求年齡和(Integer類型的求和簡單示例)
1.實現(xiàn)
// 按學(xué)生姓名分組求年齡和
public static void main(String[] args) {
Map<String, Integer> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getName
, Collectors.summingInt(Student::getAge)));
System.out.println(ageGroup);
}
執(zhí)行結(jié)果:
{張三=37, 張三3=41, 張三2=20}
三、按學(xué)生姓名分組求身高和(Collectors沒有封裝對應(yīng)的API)
1.實現(xiàn)一(推薦寫法)
思路:先分組,再map轉(zhuǎn)換成身高BigDecimal,再用reduce進(jìn)行求和
public static void main(String[] args) {
Map<String, BigDecimal> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getName
, Collectors.mapping(Student::getStature, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));
System.out.println(ageGroup);
}
執(zhí)行結(jié)果:
{張三=370, 張三3=342, 張三2=180}
2.實現(xiàn)二
思路:先分組,再收集成list,然后再map,再求和
public static void main(String[] args) {
Map<String, BigDecimal> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getName
, Collectors.collectingAndThen(Collectors.toList()
, x -> x.stream().map(Student::getStature).reduce(BigDecimal.ZERO, BigDecimal::add))));
System.out.println(ageGroup);
}
執(zhí)行結(jié)果:
{張三=370, 張三3=342, 張三2=180}
3.實現(xiàn)三
思路:業(yè)務(wù)時常在分組后需要做一些判斷邏輯再進(jìn)行累加業(yè)務(wù)計算,所以自己實現(xiàn)一個收集器
1.封裝一個自定義收集器
public class MyCollector {
static final Set<Collector.Characteristics> CH_CONCURRENT_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_CONCURRENT_NOID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED));
static final Set<Collector.Characteristics> CH_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_UNORDERED_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
private MyCollector() {
}
@SuppressWarnings("unchecked")
private static <I, R> Function<I, R> castingIdentity() {
return i -> (R) i;
}
/**
* @param <T> 集合元素類型
* @param <A> 中間結(jié)果容器
* @param <R> 最終結(jié)果類型
*/
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, // 產(chǎn)生結(jié)果容器
BiConsumer<A, T> accumulator, // 累加器
BinaryOperator<A> combiner, // 將多個容器結(jié)果合并成一個
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;
}
}
public static <T> Collector<T, ?, BigDecimal> summingDecimal(ToDecimalFunction<? super T> mapper) {
return new MyCollector.CollectorImpl<>(
() -> new BigDecimal[1],
(a, t) -> {
if (a[0] == null) {
a[0] = BigDecimal.ZERO;
}
a[0] = a[0].add(Optional.ofNullable(mapper.applyAsDecimal(t)).orElse(BigDecimal.ZERO));
},
(a, b) -> {
a[0] = a[0].add(Optional.ofNullable(b[0]).orElse(BigDecimal.ZERO));
return a;
},
a -> a[0], CH_NOID);
}
}
2.封裝一個函數(shù)式接口
@FunctionalInterface
public interface ToDecimalFunction<T> {
BigDecimal applyAsDecimal(T value);
}
3.使用
public static void main(String[] args) {
Map<String, BigDecimal> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getName
, MyCollector.summingDecimal(Student::getStature)));
System.out.println(ageGroup);
}
總結(jié)
自定義實現(xiàn)收集器可以參考Collectors的內(nèi)部類CollectorImpl的源碼,具體解析寫到注釋中。
推薦通過模仿Collectors.summingInt()的實現(xiàn)來實現(xiàn)我們自己的收集器。
// T代表流中元素的類型,A是中間處理臨時保存類型,R代表返回結(jié)果的類型
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);
}
// 這里提供一個初始化的容器,用于存儲每次累加。即使我們求和這里也只能使用容器存儲,否則后續(xù)計算累加結(jié)果會丟失(累加結(jié)果不是通過返回值方式修改的)。
@Override
public Supplier<A> supplier() {
return supplier;
}
// 累加計算:累加流中的每一個元素T到A容器存儲的結(jié)果中,這里沒有返回值,所以A必須是容器,避免數(shù)據(jù)丟失
@Override
public BiConsumer<A, T> accumulator() {
return accumulator;
}
// 這里是當(dāng)開啟parallelStream()并發(fā)處理時,會得到多個結(jié)果容器A,這里對多個結(jié)果進(jìn)行合并
@Override
public BinaryOperator<A> combiner() {
return combiner;
}
// 這里是處理中間結(jié)果類型轉(zhuǎn)換成返回結(jié)果類型
@Override
public Function<A, R> finisher() {
return finisher;
}
// 這里標(biāo)記返回結(jié)果的數(shù)據(jù)類型,這里取值來自于Collector接口的內(nèi)部類Characteristics
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
}
enum Characteristics {
// 表示此收集器是 并發(fā)的 ,這意味著結(jié)果容器可以支持與多個線程相同的結(jié)果容器同時調(diào)用的累加器函數(shù)。
CONCURRENT,
// 表示收集操作不承諾保留輸入元素的遇到順序。
UNORDERED,
// 表示整理器功能是身份功能,可以被刪除。
IDENTITY_FINISH
}補充例子:求相同姓名的學(xué)生的年齡之和(姓名組合)
package com.TestStream;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author 林高祿
* @create 2020-06-09-9:29
*/
public class Demo2 {
public static void main(String[] args) {
List<Student> studentList = StudentUtil2.createStudentList();
// 通過姓名分組,姓名為key,相同姓名的學(xué)生為列表
Map<String, List<Student>> collect = studentList.stream().collect(Collectors.groupingBy(Student::getName, Collectors.toList()));
// collect的數(shù)據(jù)為
System.out.println("collect的數(shù)據(jù)為:");
collect.forEach((key,list)-> {
System.out.println("key:"+key);
list.forEach(System.out::println);
});
// 分組后的年齡和為
System.out.println("分組后的年齡和為:");
collect.forEach((key,list)-> System.out.println("key:"+key+",年齡和"+list.stream().mapToInt(Student::getAge).sum()));
}
}
運行輸出:
collect的數(shù)據(jù)為:
key:陳文文
Student{no=1, name='陳文文', age=10, mathScore=100.0, chineseScore=90.0}
Student{no=2, name='陳文文', age=20, mathScore=90.0, chineseScore=70.0}
key:林高祿
Student{no=1, name='林高祿', age=20, mathScore=90.5, chineseScore=90.5}
Student{no=11, name='林高祿', age=20, mathScore=90.5, chineseScore=90.5}
Student{no=2, name='林高祿', age=10, mathScore=80.0, chineseScore=90.0}
Student{no=1, name='林高祿', age=30, mathScore=90.5, chineseScore=90.0}
key:1林高祿
Student{no=1, name='1林高祿', age=20, mathScore=90.5, chineseScore=90.5}
key:蔡金鑫
Student{no=1, name='蔡金鑫', age=30, mathScore=80.0, chineseScore=90.0}
分組后的年齡和為:
key:陳文文,年齡和30
key:林高祿,年齡和80
key:1林高祿,年齡和20
key:蔡金鑫,年齡和30
到此這篇關(guān)于java stream實現(xiàn)分組BigDecimal求和以及自定義分組求和的文章就介紹到這了,更多相關(guān)java stream分組BigDecimal求和內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot+thymeleaf+Echarts+Mysql 實現(xiàn)數(shù)據(jù)可視化讀取的示例
本文主要介紹了SpringBoot+thymeleaf+Echarts+Mysql 實現(xiàn)數(shù)據(jù)可視化讀取的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
詳解SpringBoot 快速整合Mybatis(去XML化+注解進(jìn)階)
本篇文章主要介紹了詳解SpringBoot 快速整合Mybatis(去XML化+注解進(jìn)階),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
Mybatis反向工程出現(xiàn)BigDecimal類型問題及解決
這篇文章主要介紹了Mybatis反向工程出現(xiàn)BigDecimal類型問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-09-09
maven中央倉庫修改驗證方式導(dǎo)致用戶名密碼失效的解決方式
這篇文章主要介紹了maven中央倉庫修改驗證方式導(dǎo)致用戶名密碼失效的解決方式,文中通過圖文結(jié)合的方式講解的非常詳細(xì),對大家解決問題有一定的幫助2024-11-11
java 字符串轉(zhuǎn)化為字符數(shù)組的3種實現(xiàn)案例
這篇文章主要介紹了java 字符串轉(zhuǎn)化為字符數(shù)組的3種實現(xiàn)案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
SpringBoot?容器刷新前回調(diào)ApplicationContextInitializer
這篇文章主要為大家介紹了SpringBoot?容器刷新前回調(diào)ApplicationContextInitializer使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Java中的do while循環(huán)控制語句基本使用
這篇文章主要介紹了Java中的do while循環(huán)控制語句基本使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01

