文章
问答
冒泡
java.util.Arrays工具类初始化的List问题

前言:今天发现后台日志中有一个java.lang.UnsupportedOperationException 的异常,信息如下

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:148)
	at java.util.AbstractList.add(AbstractList.java:108)

于是自己一下子好奇了起来,为什么List集合会抛出一个不支持操作的异常呢,思索来思索去无法明白,后来经过深入查询时发现,自己在初始化list集合的时候,误使用了java.util.Arrays.asList()这个方法。由于平时自己初始化list的时候都是使用google.common.collect包下的newArrayList()方法或者Stream.of().collect(Collectors.toList())方法,没太关注过Arrays这个工具包里真正的实现,所以导致了这个异常的发生。

 

1,复现过程

    public static void main(String[] args) {
        List<String> arr1 = Lists.newArrayList("1", "2", "3");
        List<String> arr2 = Stream.of("1", "2", "3").collect(Collectors.toList());
        List<String> arr3 = Arrays.asList("1", "2", "3");
    }

我同时使用了三种方法初始化一个集合,发现此时没有任何问题,都正常返回了List。

这时候我再全部增加一个添加的动作,发现就抛出了UnsupportedOperationException异常

    public static void main(String[] args) {
        List<String> arr1 = Lists.newArrayList("1", "2", "3");
        arr1.add("4");
        List<String> arr2 = Stream.of("1", "2", "3").collect(Collectors.toList());
        arr2.add("4");
        List<String> arr3 = Arrays.asList("1", "2", "3");
        arr3.add("4");
    }

2,原因

跟到源码中我发现,Lists和Stream.of.collect都是返回了一个ArrayList实例,但Lists却有所不同。

    // Lists静态实例化方法

    public static <E> ArrayList<E> newArrayList(E... elements) {
        Preconditions.checkNotNull(elements);
        int capacity = computeArrayListCapacity(elements.length);
        ArrayList<E> list = new ArrayList(capacity);
        Collections.addAll(list, elements);
        return list;
    }


    // Collectors.toList 返回了Collector<T, ?, List<T>> value是java.util.List

    public static <T>
    	Collector<T, ?, List<T>> toList() {
        	return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
    }


 

   // Arrays.asList也返回了一个List实例   
    @SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }


 

虽然Arrays.AsList也返回了一个List实例,但是这个List实例却不是我们平常使用的java.util.ArrayList,而是Arrays的一个内部类,这个类虽然继承了AbstractList抽象类,但是内部只实现了几个查询方法,其他的添加删除等操作方法都没有提供,所以调用Arrays.asList返回的实例任何的对集合实质操作方法,都会报操作方法找不到的异常。

 // 内部类
private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public Object[] toArray() {
            return a.clone();
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size)
                return Arrays.copyOf(this.a, size,
                                     (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }

        @Override
        public E get(int index) {
            return a[index];
        }

        @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }

        @Override
        public int indexOf(Object o) {
            E[] a = this.a;
            if (o == null) {
                for (int i = 0; i < a.length; i++)
                    if (a[i] == null)
                        return i;
            } else {
                for (int i = 0; i < a.length; i++)
                    if (o.equals(a[i]))
                        return i;
            }
            return -1;
        }

        @Override
        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }

        @Override
        public Spliterator<E> spliterator() {
            return Spliterators.spliterator(a, Spliterator.ORDERED);
        }

        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            for (E e : a) {
                action.accept(e);
            }
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            E[] a = this.a;
            for (int i = 0; i < a.length; i++) {
                a[i] = operator.apply(a[i]);
            }
        }

        @Override
        public void sort(Comparator<? super E> c) {
            Arrays.sort(a, c);
        }
    }

 

3,结论

这是一个坑点,开发的过程中完全不会注意到,没有任何报错,但是一旦触发了操作方法,就会有异常抛出来,找问题的时候也很难找到,以后还是要多注意工具类要先看一遍再使用啊。


关于作者

Dane.shang
快30岁了还没去过酒吧
获得点赞
文章被阅读