一个人行走的范围,就是他的世界。——北岛

我们在使用Collectors.groupingBy时会遇到这种情况:

1
Map<String, List<User>> map = Arrays.asList(new User(), null).stream().collect(Collectors.groupingBy(User::getName));

image-20220109213438674

为了避免这种情况,于是我自己实现了一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@SafeVarargs
@SuppressWarnings("unchecked")
public static <T, K, D, A, M extends Map<K, D>> M listGroupBy(List<T> list, Function<T, K> sFunction, Collector<? super T, A, D> downstream, boolean isParallel, Consumer<T>... peeks) {
boolean hasFinished = downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH);
return peekStream(list, isParallel, peeks).collect(new Collector<T, HashMap<K, A>, M>() {
@Override
public Supplier<HashMap<K, A>> supplier() {
return HashMap::new;
}

@Override
public BiConsumer<HashMap<K, A>, T> accumulator() {
return (m, t) -> {
K key = Optional.ofNullable(t).map(sFunction).orElse(null);
A container = m.computeIfAbsent(key, k -> downstream.supplier().get());
downstream.accumulator().accept(container, t);
};
}

@Override
public BinaryOperator<HashMap<K, A>> combiner() {
return (m1, m2) -> {
for (Map.Entry<K, A> e : m2.entrySet()) {
m1.merge(e.getKey(), e.getValue(), downstream.combiner());
}
return m1;
};
}

@Override
public Function<HashMap<K, A>, M> finisher() {
return hasFinished ? i -> (M) i : intermediate -> {
// a-> a[0]
intermediate.replaceAll((k, v) -> (A) downstream.finisher().apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
}

@Override
public Set<Characteristics> characteristics() {
return hasFinished ? Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH)) : Collections.emptySet();
}
});
}

使用方式:

1
Map<String, List<User>> map = listGroupBy(Arrays.asList(new User(), null), User::getName, Collectors.toList(), false)

这样避免了抛出异常,返回了对null值友好的结果(map里包含一个keynull的结果)

我稍作修改放到MPSimpleQueryhutool中的CollStreamUtil以及CollectorUtil中去了