ArrayList 交集运算的坑 `UnsupportedOperationException`
现场在业务中有个求List交集的运算,使用 java 官方的提供的 retainAll 方法居然报错UnsupportedOperationException。代码public static void main(String[] args) {List<Long> a = Arrays.asList(1L,2L,3L);List<Long> b = Arrays.asLis
数学集合中有交、并、差运算。java有一个完整的集合类,那么作为集合,他们支持交、并、差运算吗?答案是肯定的。
现场
业务中有个求List
交集的运算,使用 java
官方的提供的 retainAll
方法居然报错UnsupportedOperationException
。
代码
public static void main(String[] args) {
List<Long> a = Arrays.asList(1L,2L,3L);
List<Long> b = Arrays.asList(4L,3L,4L,6L);
List<Long> c = Arrays.asList(5L,2L,3L);
List<Long> res = Arrays.asList(3L,2L);
List<List<Long>> d = Arrays.asList(a, b, c);
a.retainAll(b);
System.out.println(a);
d.forEach(res::retainAll);
System.out.println(res);
}
运行后的错误
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(AbstractList.java:161)
at java.util.AbstractList$Itr.remove(AbstractList.java:374)
at java.util.AbstractCollection.retainAll(AbstractCollection.java:410)
at com.ghostcloud.jkwserver.components.router.AdjacencyListUtils.main(AdjacencyListUtils.java:349)
从截图很容易看出,代码编译没有报错,执行时报错,不科学呀。
网上list
求交集demo
public static void main(String[] args) {
List<String> listA = new ArrayList<String>();
List<String> listB = new ArrayList<String>();
listA.add("A");
listA.add("B");
listB.add("B");
listB.add("C");
listA.retainAll(listB);
System.out.println(listA);
}
运行结果为:[B]
为什么我写的代码会报错呢?
对比代码,难道是创建List
的方法不一样?
修改代码
解救
代码
public static void main(String[] args) {
List<Long> a = new ArrayList<>();
Collections.addAll(a,1L,2L,3L);
List<Long> b = new ArrayList<>();
Collections.addAll(b,4L,3L,4L,6L);
List<Long> c = new ArrayList<>();
Collections.addAll(c,5L,2L,3L);
List<Long> res = new ArrayList<>();
Collections.addAll(res,3L,2L);
List<List<Long>> d = new ArrayList<>();
Collections.addAll(d,a, b, c);
res.retainAll(b);
System.out.println(a);
d.forEach(res::retainAll);
System.out.println(res);
}
运行结果:[1, 2, 3]
[3]
开发任务解决了,但是为什么呢?
分析
异常分析
为什么会发生 java.lang.UnsupportedOperationException
异常?
分析堆栈,异常最终是在at java.util.AbstractList.remove(AbstractList.java:161)
处抛出。
两种不同方式创建的List
,调用 remove
方法,结果有差别,难道说创建的 List
有差异?接下来查看源码对比一下。
Arrays.asList()
源码
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
/**
* @serial include
*/
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) {
...
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) {
...
}
@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) {
...
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
}
Arrays.asList()
使用的 ArrayList
是 Arrays
内部类,继承并实现了 AbstractList
中的一些方法。但是没有实现 remove
方法,调用时当执行 AbstractList
的 remove
方法,即我们看到的堆栈信息at java.util.AbstractList.remove(AbstractList.java:161)
。
AbstractList
的 remove
方法
public E remove(int index) {
throw new UnsupportedOperationException();
}
AbstractList
的 remove
方法直接抛出了这个错误。
ArrayList
的remove
源码
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
...
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
...
}
结论
Arrays.asList()
使用的是内部类ArrayList
,交集运算 retainAll
最终调用其父类AbstractList
的 retainAll
、remove
方法去除非交集元素,直接抛出错误UnsupportedOperationException
。而ArrayList
的 remove
有自己具体的实现,不会调用 AbstractList
的remove
方法,故不会报错。
更多推荐
所有评论(0)