前言:今天发现后台日志中有一个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,结论
这是一个坑点,开发的过程中完全不会注意到,没有任何报错,但是一旦触发了操作方法,就会有异常抛出来,找问题的时候也很难找到,以后还是要多注意工具类要先看一遍再使用啊。