美文网首页java全栈
java8 Stream Collectors.toMap 引发

java8 Stream Collectors.toMap 引发

作者: snoweek | 来源:发表于2018-08-11 15:43 被阅读1037次

    NullPointerException

    v 为null,报空指针错误



    k为null, 运行正常。


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

    查看API可以看到

     public static <T, K, U, M extends Map<K, U>>
        Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction,
                                    Supplier<M> mapSupplier) {
            BiConsumer<M, T> 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<? super V, ? super V, ? extends V> remappingFunction) {
            if (value == null)
                throw new NullPointerException();
            if (remappingFunction == null)
                throw new NullPointerException();
            int hash = hash(key);
            Node<K,V>[] tab; Node<K,V> first; int n, i;
            int binCount = 0;
            TreeNode<K,V> t = null;
            Node<K,V> 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<K,V>)first).getTreeNode(hash, key);
                else {
                    Node<K,V> 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

    查看toMap的API及相关注释

    /*
     * <p>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 <T, K, U>
        Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                        Function<? super T, ? extends U> 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<TestDO> 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)));
        }
    }
    
    

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

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

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

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

    相关文章

      网友评论

        本文标题:java8 Stream Collectors.toMap 引发

        本文链接:https://www.haomeiwen.com/subject/pmedbftx.html