From 56129cba7bdf5ded534a6c85cd4edbd6a2624ae9 Mon Sep 17 00:00:00 2001 From: VampireAchao Date: Sat, 8 Jan 2022 14:17:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E5=AF=B9null=E5=80=BC?= =?UTF-8?q?=E5=8F=8B=E5=A5=BD=E7=9A=84groupingBy=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E7=9A=84{@link=20Collector}=E5=AE=9E=E7=8E=B0=EF=BC=8C?= =?UTF-8?q?=E5=8F=AF=E6=8C=87=E5=AE=9Amap=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/collection/CollStreamUtil.java | 16 ++--- .../cn/hutool/core/stream/CollectorUtil.java | 72 ++++++++++++++++++- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java index 51a9c3eee..e080ab563 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java @@ -3,16 +3,10 @@ package cn.hutool.core.collection; import cn.hutool.core.lang.Opt; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.stream.CollectorUtil; import cn.hutool.core.stream.StreamUtil; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collector; @@ -161,7 +155,7 @@ public class CollStreamUtil { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return groupBy(collection, key1, Collectors.groupingBy(key2, Collectors.toList()), isParallel); + return groupBy(collection, key1, CollectorUtil.groupingBy(key2, Collectors.toList()), isParallel); } /** @@ -236,7 +230,7 @@ public class CollStreamUtil { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return groupBy(collection, key, Collectors.mapping(value, Collectors.toList()), isParallel); + return groupBy(collection, key, Collectors.mapping(v -> Opt.ofNullable(v).map(value).orElse(null), Collectors.toList()), isParallel); } /** @@ -276,7 +270,7 @@ public class CollStreamUtil { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return StreamUtil.of(collection, isParallel).collect(Collectors.groupingBy(key, downstream)); + return StreamUtil.of(collection, isParallel).collect(CollectorUtil.groupingBy(key, downstream)); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java b/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java index 58b03d2ba..86357e00a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java @@ -2,9 +2,11 @@ package cn.hutool.core.stream; import cn.hutool.core.util.StrUtil; -import java.util.Collections; -import java.util.StringJoiner; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collector; /** @@ -61,4 +63,70 @@ public class CollectorUtil { Collections.emptySet() ); } + + + /** + * 提供对null值友好的groupingBy操作的{@link Collector}实现,可指定map类型 + * + * @param classifier 分组依据 + * @param mapFactory 提供的map + * @param downstream 下游操作 + * @param 实体类型 + * @param 实体中的分组依据对应类型,也是Map中key的类型 + * @param 下游操作对应返回类型,也是Map中value的类型 + * @param 下游操作在进行中间操作时对应类型 + * @param 最后返回结果Map类型 + * @return {@link Collector} + */ + public static > Collector groupingBy(Function classifier, + Supplier mapFactory, + Collector downstream) { + Supplier downstreamSupplier = downstream.supplier(); + BiConsumer downstreamAccumulator = downstream.accumulator(); + BiConsumer, T> accumulator = (m, t) -> { + K key = Optional.ofNullable(t).map(classifier).orElse(null); + A container = m.computeIfAbsent(key, k -> downstreamSupplier.get()); + downstreamAccumulator.accept(container, t); + }; + BinaryOperator> merger = (m1, m2) -> { + for (Map.Entry e : m2.entrySet()) { + m1.merge(e.getKey(), e.getValue(), downstream.combiner()); + } + return m1; + }; + @SuppressWarnings("unchecked") + Supplier> mangledFactory = (Supplier>) mapFactory; + + if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) { + return new SimpleCollector<>(mangledFactory, accumulator, merger, Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH))); + } else { + @SuppressWarnings("unchecked") + Function downstreamFinisher = (Function) downstream.finisher(); + Function, M> finisher = intermediate -> { + intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v)); + @SuppressWarnings("unchecked") + M castResult = (M) intermediate; + return castResult; + }; + return new SimpleCollector<>(mangledFactory, accumulator, merger, finisher, Collections.emptySet()); + } + } + + /** + * 提供对null值友好的groupingBy操作的{@link Collector}实现 + * + * @param classifier 分组依据 + * @param downstream 下游操作 + * @param 实体类型 + * @param 实体中的分组依据对应类型,也是Map中key的类型 + * @param 下游操作对应返回类型,也是Map中value的类型 + * @param 下游操作在进行中间操作时对应类型 + * @return {@link Collector} + */ + public static + Collector> groupingBy(Function classifier, + Collector downstream) { + return groupingBy(classifier, HashMap::new, downstream); + } + }