From 78ac9fcdefb4a0b3693e020377fe537b65e081ec Mon Sep 17 00:00:00 2001 From: VampireAchao Date: Sat, 15 Jan 2022 11:48:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4Collectors.toMap=E7=9A=84?= =?UTF-8?q?=E5=AF=B9null=E5=8F=8B=E5=A5=BD=E5=AE=9E=E7=8E=B0=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8DNPE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/collection/CollStreamUtil.java | 2 +- .../cn/hutool/core/stream/CollectorUtil.java | 83 +++++++++++++++++-- .../core/collection/CollStreamUtilTest.java | 7 ++ 3 files changed, 83 insertions(+), 9 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 ef3893d6d..b70f24a62 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 @@ -199,7 +199,7 @@ public class CollStreamUtil { if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { return Collections.emptyMap(); } - return groupBy(collection, key1, Collectors.toMap(key2, Function.identity(), (l, r) -> l), isParallel); + return groupBy(collection, key1, CollectorUtil.toMap(key2, Function.identity(), (l, r) -> l), isParallel); } /** 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 55e96877c..15e0d6d02 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 @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.StringJoiner; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; @@ -22,6 +23,16 @@ import java.util.stream.Collector; */ public class CollectorUtil { + /** + * 说明已包含IDENTITY_FINISH特征 为 Characteristics.IDENTITY_FINISH 的缩写 + */ + public static final Set CH_ID + = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH)); + /** + * 说明不包含IDENTITY_FINISH特征 + */ + public static final Set CH_NOID = Collections.emptySet(); + /** * 提供任意对象的Join操作的{@link Collector}实现,对象默认调用toString方法 * @@ -93,17 +104,12 @@ public class CollectorUtil { 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; - }; + BinaryOperator> merger = mapMerger(downstream.combiner()); @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))); + return new SimpleCollector<>(mangledFactory, accumulator, merger, CH_ID); } else { @SuppressWarnings("unchecked") Function downstreamFinisher = (Function) downstream.finisher(); @@ -113,7 +119,7 @@ public class CollectorUtil { M castResult = (M) intermediate; return castResult; }; - return new SimpleCollector<>(mangledFactory, accumulator, merger, finisher, Collections.emptySet()); + return new SimpleCollector<>(mangledFactory, accumulator, merger, finisher, CH_NOID); } } @@ -134,4 +140,65 @@ public class CollectorUtil { return groupingBy(classifier, HashMap::new, downstream); } + + /** + * 对null友好的 toMap 操作的 {@link Collector}实现,默认使用HashMap + * + * @param keyMapper 指定map中的key + * @param valueMapper 指定map中的value + * @param mergeFunction 合并前对value进行的操作 + * @param 实体类型 + * @param map中key的类型 + * @param map中value的类型 + * @return 对null友好的 toMap 操作的 {@link Collector}实现 + */ + public static + Collector> toMap(Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction) { + return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); + } + + /** + * 对null友好的 toMap 操作的 {@link Collector}实现 + * + * @param keyMapper 指定map中的key + * @param valueMapper 指定map中的value + * @param mergeFunction 合并前对value进行的操作 + * @param mapSupplier 最终需要的map类型 + * @param 实体类型 + * @param map中key的类型 + * @param map中value的类型 + * @param map的类型 + * @return 对null友好的 toMap 操作的 {@link Collector}实现 + */ + public static > + Collector toMap(Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction, + Supplier mapSupplier) { + BiConsumer accumulator + = (map, element) -> map.put(Opt.ofNullable(element).map(keyMapper).get(), Opt.ofNullable(element).map(valueMapper).get()); + return new SimpleCollector<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID); + } + + /** + * 用户合并map的BinaryOperator,传入合并前需要对value进行的操作 + * + * @param mergeFunction 合并前需要对value进行的操作 + * @param key的类型 + * @param value的类型 + * @param map + * @return 用户合并map的BinaryOperator + */ + public static > BinaryOperator mapMerger(BinaryOperator mergeFunction) { + return (m1, m2) -> { + for (Map.Entry e : m2.entrySet()) { + m1.merge(e.getKey(), e.getValue(), mergeFunction); + } + return m1; + }; + } + + } diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/CollStreamUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/CollStreamUtilTest.java index 7c95075af..69da448ba 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/CollStreamUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/CollStreamUtilTest.java @@ -150,6 +150,13 @@ public class CollStreamUtilTest { compare.put(2L, map2); Assert.assertEquals(compare, map); + // 对null友好 + Map> termIdClassIdStudentMap = CollStreamUtil.group2Map(Arrays.asList(null, new Student(2, 2, 1, "王五")), Student::getTermId, Student::getClassId); + Map> termIdClassIdStudentCompareMap = new HashMap>() {{ + put(null, MapUtil.of(null, null)); + put(2L, MapUtil.of(2L, new Student(2, 2, 1, "王五"))); + }}; + Assert.assertEquals(termIdClassIdStudentCompareMap, termIdClassIdStudentMap); } @Test