关于list在遍历中,做出删除操作的坑
程序员文章站
2022-06-10 23:42:40
...
首先介绍的是阿里规约上的一个案例:
建议如果有时间的同学可以去阿里云考一下这个认证,考试通过会发一个电子认证证书,有效期两年。
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
以上代码的执行结果肯定会出乎大家的意料,那么试一下把if中的“1”换成“2”,会是同样的结果吗? 请自行尝试执行。
下面介绍一个ArrayList中的一个变量 modCount 该值用于记录当前list变更操作的次数。
- 首先初始化list:
List<String> list = new ArrayList<String>();
- 然后开始赋值:
list.add("1");
list.add("2");
这个步骤会先判断空间,满足的情况下,将modCount+1,再将值放入elementdata数组中(默认长度10)。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!! 增长变更次数
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//此处变更次数增加
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
开始正文
public static void main(String[] args) {
//初始化list
List<String> list = new ArrayList<String>();
//开始赋值
list.add("1");
list.add("2");
//开始遍历
for (String item : list) {
System.out.println(item);
//先从删除1开始
if ("1".equals(item)) {
list.remove(item);
}
}
System.out.println("success");
}
- 1.初始化 & 赋值
- 2.※开始遍历list,遍历时会进行一个赋值动作。记录遍历初始的操作变更次数(后期异常判断)
- 3.遍历前先进行判断,当前游标是否已经完成任务。(注意这个地方后续会用到)
public boolean hasNext() {
return cursor != size;
}
- 4.获取下一元素会先进行判断 checkForComodification(),判断读取时原始的操作次数和现有的操作次数。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
判断通过返回当前位置的值。不通过 就是上面那个异常了。
- 5.然后进入remove,进行循环判断匹配,最后fastRemove()
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
fastRemove会操作modCount++;
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
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
}
只输出了1且成功了。为什么没有遍历到2 ?为什么没有报错?
- 6.回看第2步,因为步骤5删除元素1的时候,size已经发生变更。遍历第二次的时候判断,已经遍历完毕(当前游标=size)
hasNext | (cursor)0 != (size)2 : true |
remove | size->1;(元素1被移除) |
hasNext | (cursor)1 != (size)1 : false |
7.直接结束循环,不会发生获取到第二个元素,所以也不会进行判定异常。
public static void main(String[] args) {
//初始化list
List<String> list = new ArrayList<String>();
//开始赋值
list.add("1");
list.add("2");
//开始遍历
for (String item : list) {
System.out.println(item);
//从删除2开始
if ("2".equals(item)) {
list.remove(item);
}
}
System.out.println("success");
}
遍历结束,但是抛出了异常。
- 8.因为在删除元素2的时候,size发生了变化,导致了循环继续进行
hasNext | (cursor)0 != (size)2 : true |
hasNext | (cursor)1 != (size)2 : true |
remove | size->1;(元素1被移除) modCount++ |
hasNext | (cursor)2 != (size)1 : true |
- 9.遍历成功 然后进入步骤4.就发生了异常。
上一篇: 让我情何以堪