前言:ArrayList中有两种删除的方法,remove(int index):按下标删除;remove(Object o):删除某个元素;
这里其实删除某个对象的内部实现逻辑,也是找出这个元素的下标,按下标删除;
但是按元素删除需要注意一点,千万不要在循坏里面删除元素,会出现报错的情况:
@Test
public void getRemoveError(){
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
for (String item : list) {
if ("1".equals(item)) { list.remove(item);
}
}
System.out.println(list);
}
删除上面的元素1,在循坏里面写会报错;代码测试结果是,按这种写法只有删除数组倒数第二个元素“2”才不会报错,否则都会报错;
原因解析:for循坏本质上是走的list的迭代器循坏,内部的hasNext方法里面有个checkForComodification方法:校验修改的条数如果不等于被期待的修改数量,则报错;
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; //被期待的修改条数默认与修改条数相等
Itr() {}
//循坏时先判断hasNext返回为true
public boolean hasNext() {
//当前游标(下标)不等于数组实际长度 ,则返回true
return cursor != size; //@1
}
//hasNext为true,调用next()方法,获取下一个元素
@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();
//下标加1
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();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
//校验修改条数是否不等于被期待的修改条数
final void checkForComodification() {
if (modCount != expectedModCount) //@2
throw new ConcurrentModificationException();
}
}
上面对报错的核心代码做了相关注解,下面对核心判断条件做分析:
@1:为什么删除数组的倒数第二个元素不会报错,是因为remove方法之后,实际长度size会减一,下个元素cursor正好指向最后一个位置,这样的话cursor就等于size了,导致cursor != size 等式就不成立,返回false,这样就不会调用next()方法,也就不会报错了。
@2:反之会报错是因为cursor != size 等式成立继续调用了next()方法;modCount != expectedModCount会成立,是因为一开始expectedModCount被默认赋值为modCount ,但是调用remove方法时,modCount 加1,就会导致两者不相等报错;