Java踩坑之List的removeAll方法

本文探讨了在使用Java中List的removeAll方法时遇到的错误,详细解释了由于Arrays.asList和Collections.singletonList返回的不是标准ArrayList导致的问题,以及如何通过创建新的ArrayList实例避免此类问题。错误的根本原因是对源码理解不深入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    最近在公司写东西,发现List的removeAll方法报错 Demo代码如下:

                List<Long> ids1 = Arrays.asList(1L, 3L, 2L);
        List<Long> ids2 = Collections.singletonList(2L);
List<Long> ids3 = new ArrayList<>();
ids3.add(1L);
ids3.add(2L);
List<Long> ids = new ArrayList<>();
ids.add(2L);
System.out.println("==== 001");
ids1.removeAll(ids); // 这一步会报错
System.out.println("==== 002");
ids2.removeAll(ids); // 这一步也会报错
System.out.println("==== 003");
ids3.removeAll(ids);

    001报错的原因是:Arrays.asList 返回的List是自己内部实现的ArrayList 而不是util下的ArrayList对象

            /**
* Returns a fixed-size list backed by the specified array. (Changes to //明确指出 返回的是固定大小的list
* the returned list "write through" to the array.) This method acts
* as bridge between array-based and collection-based APIs, in
* combination with {@link Collection#toArray}. The returned list is
* serializable and implements {@link RandomAccess}.
*
* <p>This method also provides a convenient way to create a fixed-size
* list initialized to contain several elements:
* <pre>
* List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
* </pre>
*
* @param <T> the class of the objects in the array
* @param a the array by which the list will be backed
* @return a list view of the specified array
*/
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
            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;
}
...... //看的出来,这个list是可以修改的 但是要通过set方法
}

    所以调用removeAll方法的时候 会调用AbstractList的父类AbstractCollection的removeAll方法:

            public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}

    ArrayList是数组 在循环的时候删除元素 一定会出现问题


    002报错的原因是与001类似:Collections.singletonList的返回值SingletonSet也是自己的内部类

        
          

/**
* Returns an immutable list containing only the specified object. // 返回不可变的list
* The returned list is serializable.
*
* @param <T> the class of the objects in the list
* @param o the sole object to be stored in the returned list.
* @return an immutable list containing only the specified object.
* @since 1.3
*/
public static <T> List<T> singletonList(T o) {
return new SingletonList<>(o);
}

        
          

private static class SingletonList<E>
extends AbstractList<E>
implements RandomAccess, Serializable {

private static final long serialVersionUID = 3093736618740652951L;

private final E element;

SingletonList(E obj) {element = obj;}

public Iterator<E> iterator() {
return singletonIterator(element);
}

public int size() {return 1;}

public boolean contains(Object obj) {return eq(obj, element);}

public E get(int index) {
if (index != 0)
throw new IndexOutOfBoundsException("Index: "+index+", Size: 1");
return element;
}

// Override default methods for Collection
@Override
public void forEach(Consumer<? super E> action) {
action.accept(element);
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException();
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new UnsupportedOperationException();
}
@Override
public void sort(Comparator<? super E> c) {
}
@Override
public Spliterator<E> spliterator() {
return singletonSpliterator(element);
}
}

    // 可以看出 这个list是一个只读的list 并未对外提供编辑方法

    同样会调用AbstractCollection的removeAll方法

    

    003是我new的ArrayList对象,会调用自己内部重写的removeAll方法,不会出问题,解决001、002的问题 最简单的办法可以用new ArrayList()包一层就ok了!


    究其原因还是自己对源码的研究不足!





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpongo11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值