前言
隨著微服務(wù)的發(fā)展,越來越多的sql處理被放到j(luò)ava來處理,數(shù)據(jù)庫經(jīng)常會使用到對集合中的數(shù)據(jù)進行分組求和,分組運算等等。
那怎么樣使用java的stream優(yōu)雅的進行分組求和或運算呢?
一、準(zhǔn)備測試數(shù)據(jù)
這里測試數(shù)據(jù)學(xué)生,年齡類型是Integer,身高類型是BigDecimal,我們分別對身高個年齡進行求和。
@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進行求和
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ù)時常在分組后需要做一些判斷邏輯再進行累加業(yè)務(wù)計算,所以自己實現(xiàn)一個收集器文章來源:http://www.zghlxwxcb.cn/news/detail-790400.html
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)我們自己的收集器。文章來源地址http://www.zghlxwxcb.cn/news/detail-790400.html
// 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é)果進行合并
@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
}
到了這里,關(guān)于java stream實現(xiàn)分組BigDecimal求和,自定義分組求和的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!