android arraylist 删除元素,ArrayList中remove方法的坑

博客详细分析了在Java中使用ArrayList时遇到的`ConcurrentModificationException`异常,解释了该异常产生的原因,并通过源码分析指出在迭代过程中直接调用ArrayList的`remove()`方法会导致迭代器的expectedModCount与modCount不匹配。提供了解决方案,即在迭代过程中使用Iterator自身的`remove()`方法,以保持expectedModCount与modCount的一致性,从而避免异常。

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

fabdd57955d6

秀秀.jpg

问题

ArrayList是我们经常在代码中使用的集合类,但是ArrayList在执行remove方法时会出现ConcurrentModificationException。针对这个问题进行代码分析。

例子

public static void main(String[] args) {

List list = new ArrayList();

list.add("a");

list.add("b");

list.add("c");

list.add("d");

list.add("e");

list.add("f");

list.add("g");

list.add("h");

Iterator it = list.iterator();

while (it.hasNext()) {

String str = it.next();

if (str.equals("f")) {

list.remove(str);

}

}

}

结果:

fabdd57955d6

1.png

说明:在Debug执行过程中发现,ArrayList在执行remove方法移除元素之后,再执行it.next这句话抛出了异常。

源码分析

ArrayList的remove方法:

public E remove(int index) {

// 先检查下标索引是是否越界

rangeCheck(index);

// ArrayList的修改次数加1

modCount++;

// 获取索引对应的元素值

E oldValue = elementData(index);

// 获取删除元素后,需要移动的元素的个数

int numMoved = size - index - 1;

if (numMoved > 0)

// 将元素进行移动拷贝

System.arraycopy(elementData, index+1, elementData, index,

numMoved);

// 最后将多出的位置设置为空,这样说明是没有引用的对象了

elementData[--size] = null; // Let gc do its work

// 返回删除的旧值

return oldValue;

}

说明:思想是ArrayList的删除方法,将指定位置元素删除,然后将当前位置后面的元素向前拷贝的方式移动。但是注意一点细节,modCount++这步操作,将ArrayList的修改次数加1。而后面遍历时发现是通过使用这个字段来判断,当前的集合类是否被并发修改。

ArrayList中Iterator迭代器的实现

private class Itr implements Iterator {

int cursor; // index of next element to return

int lastRet = -1; // index of last element returned; -1 if no such

int expectedModCount = modCount;

public boolean hasNext() {

return cursor != size;

}

@SuppressWarnings("unchecked")

public E next() {

checkForComodification();

int i = cursor;

if (i >= size)

throw new NoSuchElementException();

Object[] elementData = ArrayList.this.elementData;

if (i >= elementData.length)

throw new ConcurrentModificationException();

cursor = i + 1;

return (E) elementData[lastRet = i];

}

public void remove() {

if (lastRet < 0)

throw new IllegalStateException();

checkForComodification();

try {

ArrayList.this.remove(lastRet);

cursor = lastRet;

lastRet = -1;

expectedModCount = modCount;

} catch (IndexOutOfBoundsException ex) {

throw new ConcurrentModificationException();

}

}

final void checkForComodification() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

}

}

说明:

① 在初始化Itr时expectedModCount = modCount = 8 。

② 在执行next方法的第一步先进行了checkForComodification方法的检查,因为我们之前进行了remove操作,那么modCount数值减一,实际modCount = 7 。

③ modCount 数值和expectedModCount 数值不相等,抛出ConcurrentModificationException异常。

正确写法

使用Itr自身提供的remove方法:

public class MyTest {

public static void main(String[] args) {

List list = new ArrayList();

list.add("a");

list.add("b");

list.add("c");

list.add("d");

list.add("e");

list.add("f");

list.add("g");

list.add("h");

Iterator it = list.iterator();

while (it.hasNext()) {

String str = it.next();

if (str.equals("f")) {

it.remove();

}

}

System.out.println(list);

}

}

结果:

fabdd57955d6

2.png

说明:使用Itr提供的remove方法,可以发现expectedModCount是随着modCount变化而发生变化的,所以是不需要考虑modCount修改次数不一致的问题。

思考

foreach中的remove方法实际上使用list.remove一样会报ConcurrentModificationException异常。因为foreach在jvm中还是会解析成Iterator来执行的,实际上和错误例子是一样的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值