java8 Stream Collectors.toMap 引发的 NullPointerException和Duplicate key

NullPointerException

v 为null,报空指针错误


java8 Stream Collectors.toMap 引发的 NullPointerException和Duplicate key_第1张图片

java8 Stream Collectors.toMap 引发的 NullPointerException和Duplicate key_第2张图片

k为null, 运行正常。

java8 Stream Collectors.toMap 引发的 NullPointerException和Duplicate key_第3张图片

以往的认知:HashMap中k,v都是可以存null值的。在上面的测试用例中可以看到,v为null其实会报错。

查看API可以看到

 public static >
    Collector toMap(Function keyMapper,
                                Function valueMapper,
                                BinaryOperator mergeFunction,
                                Supplier mapSupplier) {
        BiConsumer accumulator
                = (map, element) -> map.merge(keyMapper.apply(element),
                                              valueMapper.apply(element), mergeFunction);
        return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
    }
@Override
    public V merge(K key, V value,
                   BiFunction remappingFunction) {
        if (value == null)
            throw new NullPointerException();
        if (remappingFunction == null)
            throw new NullPointerException();
        int hash = hash(key);
        Node[] tab; Node first; int n, i;
        int binCount = 0;
        TreeNode t = null;
        Node old = null;
        if (size > threshold || (tab = table) == null ||
            (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((first = tab[i = (n - 1) & hash]) != null) {
            if (first instanceof TreeNode)
                old = (t = (TreeNode)first).getTreeNode(hash, key);
            else {
                Node e = first; K k;
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k)))) {
                        old = e;
                        break;
                    }
                    ++binCount;
                } while ((e = e.next) != null);
            }
        }
        if (old != null) {
            V v;
            if (old.value != null)
                v = remappingFunction.apply(old.value, value);
            else
                v = value;
            if (v != null) {
                old.value = v;
                afterNodeAccess(old);
            }
            else
                removeNode(hash, key, null, false, true);
            return v;
        }
        if (value != null) {
            if (t != null)
                t.putTreeVal(this, tab, hash, key, value);
            else {
                tab[i] = newNode(hash, key, value, first);
                if (binCount >= TREEIFY_THRESHOLD - 1)
                    treeifyBin(tab, hash);
            }
            ++modCount;
            ++size;
            afterNodeInsertion(true);
        }
        return value;
    }

从代码中可以看到,HashMap.merge 会对v进行判空。

目前我的解决办法是在流中加上判空过滤

System.out.println(list.stream().filter(t -> t.getName() != null).collect(Collectors.toMap(TestDO::getId, TestDO::getName, (k1, k2) -> k1)));

Duplicate key

java8 Stream Collectors.toMap 引发的 NullPointerException和Duplicate key_第4张图片
java8 Stream Collectors.toMap 引发的 NullPointerException和Duplicate key_第5张图片

查看toMap的API及相关注释

/*
 * 

If the mapped keys contains duplicates (according to * {@link Object#equals(Object)}), an {@code IllegalStateException} is * thrown when the collection operation is performed. If the mapped keys * may have duplicates, use {@link #toMap(Function, Function, BinaryOperator)} * instead. */ public static Collector> toMap(Function keyMapper, Function valueMapper) { return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new); }

如果有重复的key,使用toMap(Function, Function)会抛异常,可以使用toMap(Function, Function, BinaryOperator)来解决。

优化后的代码是

public class MailTest {
    public static void main(String[] args) {
        List list = Lists.newArrayList();
        TestDO t1 = new TestDO(1, "a");
        TestDO t2 = new TestDO(1, "b");
        list.add(t1);
        list.add(t2);
        System.out.println(list.stream().collect(Collectors.toMap(TestDO::getId, TestDO::getName, (k1, k2) -> k1)));
        System.out.println(list.stream().collect(Collectors.toMap(TestDO::getId, TestDO::getName, (k1, k2) -> k2)));
        System.out.println(list.stream().collect(Collectors.toMap(TestDO::getId, TestDO::getName, (k1, k2) -> k1 + k2)));
    }
}

java8 Stream Collectors.toMap 引发的 NullPointerException和Duplicate key_第6张图片

从执行结果来看,对于重复的key,对他的value可以有三种取值情况:

  1. (k1, k2) -> k1,取第一个值

  2. (k1, k2) -> k2),取第二个值

  3. (k1, k2) -> k1 + k2),对两个值做某些操作后取结果。(例如基本类型可以进行加减乘除)。

你可能感兴趣的:(java8 Stream Collectors.toMap 引发的 NullPointerException和Duplicate key)