ArrayList删除倒数第二个元素不报ConcurrentModificationException原因-----阿里开发手册

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/z28126308/article/details/78155003

最近看阿里的开发手册发现当迭代ArrayList时删除ArrayList的倒数第二个元素是不会报ConcurrentModificationException异常,为此个人写了一下测试代码去ArrayList源码查找了一下原因,在说明前个人觉得还是需要先介绍一下List的foreach过程。

Java在通过foreach遍历集合列表时,会先为列表创建对应的迭代器,并通过调用迭代器的hasNext()函数判断是否含下一个元素,若有则调用iterator.next()获取继续遍历,没有则结束遍历。即通过String each:list遍历相当于在List中先创建一个迭代器,然后进行if(iterator.hasNext)判断是否还含有元素,有则进行each = iterator.next()进行赋值遍历。

该文章ArrayList移除元素时涉及的方法:

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;
}
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
}
elementData为列表用于存储数据的数组,而modCount++为列表结构的修改次数,删除元素不为null时看remove(Object o)中的第二个循环,由fastRemove(int index)可以看出列表每次删除元素时修改此时modCount都会自增1。

ArrayList的iterator方法(Itr实现Iterator接口,是ArrayList的内部类,含iterator()方法的集合类都会返回相应的Iterator实现类):

 
 
public Iterator<E> iterator() {
    return new Itr();
}
该文章涉及的ArrayList迭代器Itr的主要内容如下(省略remove与forEachRemaining方法):

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;

    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];
    }


    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}
由Itr中的checkForComodification()方法可以看出当列表的修改次数(默认值为0)与希望的修改次数(0)不相等时都会抛出ConcurrentModificationException异常,而cursor为下一个返回值的列表索引,如下例中遍历到"f"时cursor为5等于列表size 5。

源码看到这应该清楚的知道遍历list时进行删除抛出错误的原因是因为modCount != expectedModCount,而删除倒数第二个不抛错的原因就在于迭代器获取元素前的hasNext()判断,当遍历到倒数第二个元素并删除该元素时将使列表的size-1并等于cursor,此时hasNext()返回false所以不再调用next()方法调用checkForComodification()进行修改验证。

扫描二维码关注公众号,回复: 4119780 查看本文章

以下是个人的测试代码,由于删除的元素位于ArrayList的倒数第二个,所有并不会抛并发修改异常:

package per.test.list;

import com.google.common.collect.Lists;
import org.junit.Test;

import java.util.List;
import java.util.Objects;

/**
 * 创建人:Wilson
 * 描述:
 * 创建日期:2017/10/3
 */
public class ListTest {
    @Test
    public void listRemoveTest() {
        List<String> list = Lists.newArrayList("a", "b", "c", "d", "e", "f");
        for (String each : list) {
            if(Objects.equals(each,"e")){
                list.remove(each);
            }
        }
    }
}



猜你喜欢

转载自blog.csdn.net/z28126308/article/details/78155003