概述
删除ArrayList数组中某个元素,通常会使用for循环匹配目标元素完成删除操作。
public void remove(List<String> list, String str){
Iterator<String> it = list.iterator();
while(it.hasNext()){
if(str.equals(it.next())){
it.remove();
}
}
}
public void remove(List<String> list, String str){
for(String s: list){
if(str.equals(it.next())){
list.remove(s);
}
}
}
以上两种写法,第一种使用了显示声明迭代器Iterator的方式来遍历数组,匹配成功后删除;第二种使用JDK语法糖foreach的方式来遍历数组,匹配成功后删除。但是第二种写法会发生ConcurrentModificationException异常,简单来讲foreach是一个语法糖,其在进行遍历的时候还是使用的是ArrayList内部自己实现的迭代器Iterator,foreach的每次遍历都相当于调用Iterator的next()方法获取下一个元素,且方法内部会校验Iterator维护的expectedModCount是否等于ArrayList类维护的modCount,如不等就会抛出ConcurrentModificationException异常,又因为直接通过ArrayList的remove()方法来删除元素只会维护modCount,所以最终导致expectedModCount和modCount不相等。
ArrayList#remove
抛开remove()方法中越界校验、移除后数组拷贝等逻辑,发现ArrayList在进行删除元素前会先将modCount属性自增加一来记录当前数组的修改次数。这就导致一边使用foreach语法糖遍历数组,一边使用ArrayList类remove()方法删除元素会发生ConcurrentModificationException异常。
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;
}
Iterator
ArrayList内部通过实现Iterator接口实现了一个迭代器,如下代码所示(省略部分代码):
- 每次调用迭代器的next()方法时,都会执行checkForComodification()方法的校验逻辑;
- checkForComodification()方法主要职责就是校验迭代器内属性expectedModCount和ArrayList类中属性modCount是否相等,不相等就抛出ConcurrentModificationException异常。
迭代器中的remove()方法,首先会调用checkForComodification()方法来校验当前数组的修改次数是否相等;然后会调用ArrayList类中的remove()方法完成删除;最后再将执行删除操作后ArrayList类中modCount属性的最新值赋给expectedModCount以保持两值相等。
private class Itr implements Iterator<E> {
int cursor;
// index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
}
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();
}
}
ArrayList#removeIf
想要删除数组中一个元素还可以使用ArrayList类的removeIf()方法,源代码逻辑可以参考下面六个步骤:
- 将modCount赋值给removeIf()方法局部变量expectedModCount;
- 使用for i 循环遍历数组每一个元素与目标删除元素比对,如果相等就将当前下标放入BitSet中标记为true(待删除元素);
- 校验局部变量expectedModCount与modCount是否相等,防止其它线程操作数组,如果两值不相等同样抛出ConcurrentModificationException异常;
- 再次for i循环遍历数组,利用BitSet完成删除目标元素、原地移动元素;
- 再校验一次局部变量expectedModCount与modCount是否相等,同样防止同时有其它线程在操作数组;
- 将modCount自增加一;
最后
以上就是英俊发夹为你收集整理的ArrayList删除元素的细则的全部内容,希望文章能够帮你解决ArrayList删除元素的细则所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复