我想我会花点时间尝试消化你遇到的这个问题。我整理了一个 SSCE,如果我手动完成,我会期望什么,以及groupingBy
实现的实际作用。
我不认为这是一个答案,但它是一个'想知道为什么它是一个问题'的事情。此外,如果您愿意,可以随意破解此代码以获得对 null 友好的收集器。
编辑:通用友好的实现:
/** groupingByNF - NullFriendly - allows you to specify your own Map and List supplier. */
private static final <T,K> Collector<T,?,Map<K,List<T>>> groupingByNF (
final Supplier<Map<K,List<T>>> mapsupplier,
final Supplier<List<T>> listsupplier,
final Function<? super T,? extends K> classifier) {
BiConsumer<Map<K,List<T>>, T> combiner = (m, v) -> {
K key = classifier.apply(v);
List<T> store = m.get(key);
if (store == null) {
store = listsupplier.get();
m.put(key, store);
}
store.add(v);
};
BinaryOperator<Map<K, List<T>>> finalizer = (left, right) -> {
for (Map.Entry<K, List<T>> me : right.entrySet()) {
List<T> target = left.get(me.getKey());
if (target == null) {
left.put(me.getKey(), me.getValue());
} else {
target.addAll(me.getValue());
}
}
return left;
};
return Collector.of(mapsupplier, combiner, finalizer);
}
/** groupingByNF - NullFriendly - otherwise similar to Java8 Collections.groupingBy */
private static final <T,K> Collector<T,?,Map<K,List<T>>> groupingByNF (Function<? super T,? extends K> classifier) {
return groupingByNF(HashMap::new, ArrayList::new, classifier);
}
考虑这段代码(代码根据 String.length() 对 String 值进行分组,(如果输入 String 为 null,则为 null)):
public static void main(String[] args) {
String[] input = {"a", "a", "", null, "b", "ab"};
// How we group the Strings
final Function<String, Integer> classifier = (a) -> {return a != null ? Integer.valueOf(a.length()) : null;};
// Manual implementation of a combiner that accumulates a string value based on the classifier.
// no special handling of null key values.
BiConsumer<Map<Integer,List<String>>, String> combiner = (m, v) -> {
Integer key = classifier.apply(v);
List<String> store = m.get(key);
if (store == null) {
store = new ArrayList<String>();
m.put(key, store);
}
store.add(v);
};
// The finalizer merges two maps together (right into left)
// no special handling of null key values.
BinaryOperator<Map<Integer, List<String>>> finalizer = (left, right) -> {
for (Map.Entry<Integer, List<String>> me : right.entrySet()) {
List<String> target = left.get(me.getKey());
if (target == null) {
left.put(me.getKey(), me.getValue());
} else {
target.addAll(me.getValue());
}
}
return left;
};
// Using a manual collector
Map<Integer, List<String>> manual = Arrays.stream(input).collect(Collector.of(HashMap::new, combiner, finalizer));
System.out.println(manual);
// using the groupingBy collector.
Collector<String, ?, Map<Integer, List<String>>> collector = Collectors.groupingBy(classifier);
Map<Integer, List<String>> result = Arrays.stream(input).collect(collector);
System.out.println(result);
}
上面的代码产生输出:
{0=[], null=[null], 1=[a, a, b], 2=[ab]}
Exception in thread "main" java.lang.NullPointerException: element cannot be mapped to a null key
at java.util.Objects.requireNonNull(Objects.java:228)
at java.util.stream.Collectors.lambda$groupingBy$135(Collectors.java:907)
at java.util.stream.Collectors$$Lambda$10/258952499.accept(Unknown Source)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at CollectGroupByNull.main(CollectGroupByNull.java:49)