Collectors.groupingBy不接受null键

Mar*_*rcG 39 java hashmap java-8 java-stream collectors

在Java 8中,这适用于:

Stream<Class> stream = Stream.of(ArrayList.class);
HashMap<Class, List<Class>> map = (HashMap)stream.collect(Collectors.groupingBy(Class::getSuperclass));
Run Code Online (Sandbox Code Playgroud)

但这不是:

Stream<Class> stream = Stream.of(List.class);
HashMap<Class, List<Class>> map = (HashMap)stream.collect(Collectors.groupingBy(Class::getSuperclass));
Run Code Online (Sandbox Code Playgroud)

Maps允许null键,List.class.getSuperclass()返回null.但Collectors.grouping可以在Collectors.java第907行发出NPE:

K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key"); 
Run Code Online (Sandbox Code Playgroud)

如果我创建自己的收集器,它的工作原理改为:

K key = classifier.apply(t);  
Run Code Online (Sandbox Code Playgroud)

我的问题是:

1)Collectors的Javadoc.GroupingBy并没有说它不应该映射一个空键.出于某种原因这种行为是否必要?

2)是否有另一种更简单的方法来接受一个空键,而不必创建我自己的收集器?

小智 44

我遇到了同样的问题.这失败了,因为groupingBy对分类器返回的值执行Objects.requireNonNull:

    Map<Long, List<ClaimEvent>> map = events.stream()
      .filter(event -> eventTypeIds.contains(event.getClaimEventTypeId()))
      .collect(groupingBy(ClaimEvent::getSubprocessId));
Run Code Online (Sandbox Code Playgroud)

使用Optional,这有效:

    Map<Optional<Long>, List<ClaimEvent>> map = events.stream()
      .filter(event -> eventTypeIds.contains(event.getClaimEventTypeId()))
      .collect(groupingBy(event -> Optional.ofNullable(event.getSubprocessId())));
Run Code Online (Sandbox Code Playgroud)


Mar*_*rcG 11

对于第一个问题,我同意skiwi它不应该抛出一个NPE.我希望他们会改变它(或者至少将它添加到javadoc).同时,为了回答第二个问题,我决定使用Collectors.toMap而不是Collectors.groupingBy:

Stream<Class<?>> stream = Stream.of(ArrayList.class);

Map<Class<?>, List<Class<?>>> map = stream.collect(
    Collectors.toMap(
        Class::getSuperclass,
        Collections::singletonList,
        (List<Class<?>> oldList, List<Class<?>> newEl) -> {
        List<Class<?>> newList = new ArrayList<>(oldList.size() + 1);
        newList.addAll(oldList);
        newList.addAll(newEl);
        return newList;
        }));
Run Code Online (Sandbox Code Playgroud)

或者,封装它:

/** Like Collectors.groupingBy, but accepts null keys. */
public static <T, A> Collector<T, ?, Map<A, List<T>>>
groupingBy_WithNullKeys(Function<? super T, ? extends A> classifier) {
    return Collectors.toMap(
        classifier,
        Collections::singletonList,
        (List<T> oldList, List<T> newEl) -> {
            List<T> newList = new ArrayList<>(oldList.size() + 1);
            newList.addAll(oldList);
            newList.addAll(newEl);
            return newList;
            });
    }
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

Stream<Class<?>> stream = Stream.of(ArrayList.class);
Map<Class<?>, List<Class<?>>> map = stream.collect(groupingBy_WithNullKeys(Class::getSuperclass));
Run Code Online (Sandbox Code Playgroud)

请注意rolfl给出了另一个更复杂的答案,它允许您指定自己的地图和列表供应商.我没有测试过.


Ila*_*n M 7

分组前使用过滤器

在groupingBy之前过滤掉空实例。

这是一个例子

MyObjectlist.stream().filter(p -> p.getSomeInstance() != null).collect(Collectors.groupingBy(MyObject::getSomeInstance));
Run Code Online (Sandbox Code Playgroud)

  • OPS的第二个问题隐式指出他们希望接受空键,因此这无法回答他的问题。 (13认同)
  • 这可能是正确答案的一部分。另一部分是您可以随后将空项目显式放入结果中:`result.put(null,myList.stream.filter(p-&gt; p.get()== null).collect(Collectors.toList()) ;`有点丑陋,但可能仍然比创建收集器更方便。 (2认同)