Fail-Fast 前奏

示例代码1

public static void main(String[] args) {
    String string = "a b c d e";
    List<String> stringList = Arrays.asList(string.split(" "));
    Iterator<String> iterator = stringList.iterator();
    while (iterator.hasNext()) {
        if(iterator.next().equals("c")) {
            stringList.remove("c");
        }
    }
}

输出:

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.remove(AbstractList.java:161)
    at java.util.AbstractList$Itr.remove(AbstractList.java:374)
    at java.util.AbstractCollection.remove(AbstractCollection.java:293)
    at DotThis.main(DotThis.java:75)

报不支持的操作异常,UnsupportedOperationException。
ArrayList.asList返回的List是固定大小的List,也就是说不可以对其进行add、remove操作。
源码如下:

//Returns a fixed-size list backed by the specified array.
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

//该ArrayList类不是java.util包中的内容,而是Arrays类的内部类。
private static class ArrayList<E> extends AbstractList<E>
    implements RandomAccess, java.io.Serializable
{
    private static final long serialVersionUID = -2764017481108945198L;
    private final E[] a;

    ArrayList(E[] array) {
        a = Objects.requireNonNull(array);
    }
    /*省略此处代码*/
}

从上面代码的注释中可以看到是 “返回的是固定大小的List”。从源码实现上可以看到,private final E[] a; ,内部类ArrayList中的成员变量a,是final的。

示例代码二

public static void main(String[] args) {
    String string = "a b c d e";
    List<String> arrList = Arrays.asList(string.split(" "));
    List<String> stringList = new ArrayList<String>(arrList);
    Iterator<String> iterator = stringList.iterator();
    while (iterator.hasNext()) {
        if(iterator.next().equals("c")) {
            stringList.remove("c");
        }
    }
}

输出:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at DotThis.main(DotThis.java:76)

每个容器源码都会有modCount,modCount用来表示容器被结构性修改的次数。所谓结构性修改是指,改变了容器的size,或者在迭代过程中产生了不正确的结果。

代码中,在迭代到”c”时,stringList.remove(“c”); 执行了remove操作,对list造成了结构性修改,改变了list的size,modCount的值会加1。
这样当迭代到”d”时,发现expectedModCount与modCount不等了,因此抛ConcurrentModificationException异常了。

示例代码三

public static void main(String[] args) {
    String string = "a b c d e";
    List<String> stringList1 = Arrays.asList(string.split(" "));
    List<String> stringList = new ArrayList<String>(stringList1);
    System.out.println(stringList);

    Iterator<String> iterator = stringList.iterator();
    while (iterator.hasNext()) {
        if(iterator.next().equals("c")) 
            iterator.remove();//这里跟上例不同,上例为stringList.remove("c");
        }
    }
    System.out.println(stringList);
}

输出:

[a, b, c, d, e]
[a, b, d, e]

查看ArrayList#Itr#remove方法:

private class Itr implements Iterator<E> {
    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();
    }
    /**省略此处代码**/    
}

从上面代码可以看到,首先也判断了expectedModCount与modCount是否相等,但是这时还没有执行remove操作,因此不会抛异常。在执行了ArrayList.this.remove(lastRet); 之后,有一个关键的一行expectedModCount = modCount; 更新了expectedModCount的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值