Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Collectors.toMap中的NullPointerException具有null条目值_Java_Nullpointerexception_Java Stream_Collectors - Fatal编程技术网

Java Collectors.toMap中的NullPointerException具有null条目值

Java Collectors.toMap中的NullPointerException具有null条目值,java,nullpointerexception,java-stream,collectors,Java,Nullpointerexception,Java Stream,Collectors,Collectors.toMap如果其中一个值为null,则抛出NullPointerException。我不理解这种行为,映射可以包含空指针作为值而没有任何问题。对于收集器.toMap,值不能为null是否有充分的理由 另外,是否有一种很好的Java8方法来解决这个问题,或者我应该恢复到普通的旧for循环 我的问题的一个例子: import java.util.ArrayList; import java.util.List; import java.util.Map; import java

Collectors.toMap
如果其中一个值为
null
,则抛出
NullPointerException
。我不理解这种行为,映射可以包含空指针作为值而没有任何问题。对于
收集器.toMap
,值不能为null是否有充分的理由

另外,是否有一种很好的Java8方法来解决这个问题,或者我应该恢复到普通的旧for循环

我的问题的一个例子:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


class Answer {
    private int id;

    private Boolean answer;

    Answer() {
    }

    Answer(int id, Boolean answer) {
        this.id = id;
        this.answer = answer;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Boolean getAnswer() {
        return answer;
    }

    public void setAnswer(Boolean answer) {
        this.answer = answer;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Answer> answerList = new ArrayList<>();

        answerList.add(new Answer(1, true));
        answerList.add(new Answer(2, true));
        answerList.add(new Answer(3, null));

        Map<Integer, Boolean> answerMap =
        answerList
                .stream()
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));
    }
}

这个问题在Java11中仍然存在。

使用
收集器的静态方法是不可能的。的javadoc解释了
toMap
基于:

@param mergeFunction一个合并函数,用于解决与同一键相关的值之间的冲突,提供给
Map#merge(Object,Object,BiFunction)}

的javadoc说:

@如果指定的键为null且此映射为空,则引发NullPointerException 不支持空键或值或重映射函数 null

通过使用列表的方法,可以避免for循环

Map<Integer,  Boolean> answerMap = new HashMap<>();
answerList.forEach((answer) -> answerMap.put(answer.getId(), answer.getAnswer()));
Map answerMap=newhashmap();
forEach((answer)->answerMap.put(answer.getId(),answer.getAnswer());
但这并不比老办法简单:

Map<Integer, Boolean> answerMap = new HashMap<>();
for (Answer answer : answerList) {
    answerMap.put(answer.getId(), answer.getAnswer());
}
Map answerMap=newhashmap();
对于(回答:回答列表){
put(answer.getId(),answer.getAnswer());
}

根据
堆栈跟踪

Exception in thread "main" java.lang.NullPointerException
at java.util.HashMap.merge(HashMap.java:1216)
at java.util.stream.Collectors.lambda$toMap$148(Collectors.java:1320)
at java.util.stream.Collectors$$Lambda$5/391359742.accept(Unknown Source)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1359)
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 com.guice.Main.main(Main.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
何时调用
map.merge

        BiConsumer<M, T> accumulator
            = (map, element) -> map.merge(keyMapper.apply(element),
                                          valueMapper.apply(element), mergeFunction);
我不经常使用Java8,所以我不知道是否有更好的方法来修复它,但是修复它有点困难

你可以做:

使用filter过滤所有空值,在Javascript代码中检查服务器是否没有为此id发送任何应答,这意味着他没有应答

大概是这样的:

Map<Integer, Boolean> answerMap =
        answerList
                .stream()
                .filter((a) -> a.getAnswer() != null)
                .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));
Map-answerMap=
回答者名单
.stream()
.filter((a)->a.getAnswer()!=null)
.collect(Collectors.toMap(Answer::getId,Answer::getAnswer));
或者使用peek,它用于为元素更改流元素。使用peek,您可以将答案更改为map更可接受的内容,但这意味着要稍微编辑一下您的逻辑

听起来,如果您想保留当前的设计,您应该避免使用
收集器。toMap

您可以在OpenJDK中使用以下方法解决此问题:

Map<Integer, Boolean> collect = list.stream()
        .collect(HashMap::new, (m,v)->m.put(v.getId(), v.getAnswer()), HashMap::putAll);
(教程对我帮助最大。)

编辑:


Collectors.toMap
不同,如果您多次使用相同的键,这将以静默方式替换值,正如@mmdemirbas在注释中指出的那样。如果您不想这样做,请查看注释中的链接。

我编写了一个
收集器
,与默认java收集器不同,当您有
null
值时,它不会崩溃:

public static <T, K, U>
        Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper,
                Function<? super T, ? extends U> valueMapper) {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                Map<K, U> result = new HashMap<>();
                for (T item : list) {
                    K key = keyMapper.apply(item);
                    if (result.putIfAbsent(key, valueMapper.apply(item)) != null) {
                        throw new IllegalStateException(String.format("Duplicate key %s", key));
                    }
                }
                return result;
            });
}
公共静态

收集器toMap(函数这里有一个比@EmmanuelTouzery建议的收集器更简单的收集器。如果您愿意,可以使用它:

public static <T, K, U> Collector<T, ?, Map<K, U>> toMapNullFriendly(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends U> valueMapper) {
    @SuppressWarnings("unchecked")
    U none = (U) new Object();
    return Collectors.collectingAndThen(
            Collectors.<T, K, U> toMap(keyMapper,
                    valueMapper.andThen(v -> v == null ? none : v)), map -> {
                map.replaceAll((k, v) -> v == none ? null : v);
                return map;
            });
}
公共静态收集器(

函数如果值是字符串,则这可能会起作用:

map.entrySet().stream().collect(Collectors.toMap(e->e.getKey(),e->Optional.ofNullable(e.getValue()).orElse(“”))

是的,我的回答太晚了,但我认为这可能有助于理解引擎盖下发生的事情,以防有人想给其他
收集器
-逻辑编码

我试图通过编写一种更自然、更直截了当的方法来解决这个问题。我认为它尽可能直接:

public class LambdaUtilities {

  /**
   * In contrast to {@link Collectors#toMap(Function, Function)} the result map
   * may have null values.
   */
  public static <T, K, U, M extends Map<K, U>> Collector<T, M, M> toMapWithNullValues(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {
    return toMapWithNullValues(keyMapper, valueMapper, HashMap::new);
  }

  /**
   * In contrast to {@link Collectors#toMap(Function, Function, BinaryOperator, Supplier)}
   * the result map may have null values.
   */
  public static <T, K, U, M extends Map<K, U>> Collector<T, M, M> toMapWithNullValues(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Supplier<Map<K, U>> supplier) {
    return new Collector<T, M, M>() {

      @Override
      public Supplier<M> supplier() {
        return () -> {
          @SuppressWarnings("unchecked")
          M map = (M) supplier.get();
          return map;
        };
      }

      @Override
      public BiConsumer<M, T> accumulator() {
        return (map, element) -> {
          K key = keyMapper.apply(element);
          if (map.containsKey(key)) {
            throw new IllegalStateException("Duplicate key " + key);
          }
          map.put(key, valueMapper.apply(element));
        };
      }

      @Override
      public BinaryOperator<M> combiner() {
        return (left, right) -> {
          int total = left.size() + right.size();
          left.putAll(right);
          if (left.size() < total) {
            throw new IllegalStateException("Duplicate key(s)");
          }
          return left;
        };
      }

      @Override
      public Function<M, M> finisher() {
        return Function.identity();
      }

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

    };
  }

}
公共类LambdaUtilities{
/**
*与{@link Collectors#toMap(函数,函数)}相反,结果映射
*可能有空值。
*/

公共静态收集器toMapWithNullValues(函数通过小调整保留所有问题ID

Map<Integer, Boolean> answerMap = 
  answerList.stream()
            .collect(Collectors.toMap(Answer::getId, a -> 
                       Boolean.TRUE.equals(a.getAnswer())));
Map-answerMap=
answerList.stream()
.collect(Collectors.toMap)(答案::getId,a->
Boolean.TRUE.equals(a.getAnswer());

很抱歉重新打开一个旧问题,但由于最近编辑了它,说“问题”仍然存在于Java 11中,我想指出以下几点:

answerList
        .stream()
        .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));
提供空指针异常,因为映射不允许空值作为值。 这是有意义的,因为如果您在映射中查找键
k
,但它不存在,那么返回的值已经是
null
(请参见javadoc)。因此,如果您能够输入
k
null
,则映射看起来会表现异常

正如有人在评论中所说,使用过滤很容易解决这个问题:

answerList
        .stream()
        .filter(a -> a.getAnswer() != null)
        .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));
通过这种方式,地图中不会插入任何
null
值,当您查找地图中没有答案的id时,仍然会得到
null
作为“值”

我希望这对每个人都有意义。

公共静态收集器toHashMap(
public static <T, K, V> Collector<T, HashMap<K, V>, HashMap<K, V>> toHashMap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper
)
{
    return Collector.of(
            HashMap::new,
            (map, t) -> map.put(keyMapper.apply(t), valueMapper.apply(t)),
            (map1, map2) -> {
                map1.putAll(map2);
                return map1;
            }
    );
}

public static <T, K> Collector<T, HashMap<K, T>, HashMap<K, T>> toHashMap(
        Function<? super T, ? extends K> keyMapper
)
{
    return toHashMap(keyMapper, Function.identity());
}
功能我有轻微的改变

此版本:

  • 允许空键
  • 允许空值
  • 检测重复的密钥(即使它们为null)并抛出与原始JDK实现相同的
    IllegalStateException
  • 当密钥已映射到空值时,也会检测重复的密钥。换句话说,将具有空值的映射与无映射分开

公共静态收集器toMapOfNullables(函数为完整起见,我发布了一个带有mergeFunction参数的toMapOfNullables版本:

public static <T, K, U> Collector<T, ?, Map<K, U>> toMapOfNullables(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) {
    return Collectors.collectingAndThen(Collectors.toList(), list -> {
        Map<K, U> result = new HashMap<>();
        for(T item : list) {
            K key = keyMapper.apply(item);
            U newValue = valueMapper.apply(item);
            U value = result.containsKey(key) ? mergeFunction.apply(result.get(key), newValue) : newValue;
            result.put(key, value);
        }
        return result;
    });
}

公共静态收集器toMapOfNullables(函数
null
总是有点问题,就像在TreeMap中一样。也许是尝试
Optional
?否则拆分并使用过滤器的好时机。@JoopEggen
null
可能是键的问题,但在这种情况下是值的问题。不是所有的映射都有
null
问题,例如
HashMap
可能有一个问题de>null
键和任意数量的
null
值,yo
answerList
        .stream()
        .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));
answerList
        .stream()
        .filter(a -> a.getAnswer() != null)
        .collect(Collectors.toMap(Answer::getId, Answer::getAnswer));
public static <T, K, V> Collector<T, HashMap<K, V>, HashMap<K, V>> toHashMap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper
)
{
    return Collector.of(
            HashMap::new,
            (map, t) -> map.put(keyMapper.apply(t), valueMapper.apply(t)),
            (map1, map2) -> {
                map1.putAll(map2);
                return map1;
            }
    );
}

public static <T, K> Collector<T, HashMap<K, T>, HashMap<K, T>> toHashMap(
        Function<? super T, ? extends K> keyMapper
)
{
    return toHashMap(keyMapper, Function.identity());
}
public static <T, K, U> Collector<T, ?, Map<K, U>> toMapOfNullables(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) {
    return Collectors.collectingAndThen(Collectors.toList(), list -> {
        Map<K, U> result = new HashMap<>();
        for(T item : list) {
            K key = keyMapper.apply(item);
            U newValue = valueMapper.apply(item);
            U value = result.containsKey(key) ? mergeFunction.apply(result.get(key), newValue) : newValue;
            result.put(key, value);
        }
        return result;
    });
}