繁体   English   中英

ArrayList的特殊行为remove() - 为什么?

[英]Peculiar behavior of ArrayList remove() - Why?

当我们删除-1并清空ArrayList它会抛出ConcurrentModificationException ,当我们从同一个空ArrayList删除0 ,它会抛出NoSuchElementException

请找到以下代码:

    public class Test {
    public static void main(String[] argv) {

        ArrayList<Integer> list = new ArrayList<Integer>();
        Iterator<Integer> it = list.iterator();
        try {
            list.remove(-1);
        } catch (IndexOutOfBoundsException e) {

        }
        try {
            it.next();// Throwing ConcurrentModificationException
        } catch (ConcurrentModificationException e) {
            System.err.println("ConcurrentModificationException 1");
        } catch (NoSuchElementException e) {
            System.err.println("NoSuchElementException 1 ");
        }

        list = new ArrayList<Integer>();
        it = list.iterator();
        try {
            list.remove(0);
        } catch (IndexOutOfBoundsException e) {
        }
        try {
            it.next();// Throwing NoSuchElementException
        } catch (NoSuchElementException e) {
            System.err.println("NoSuchElementException 2");
        } catch (ConcurrentModificationException e) {
            System.err.println("ConcurrentModificationException 2 ");
        }

    }
}

从我的理解NoSuchElementException是好的,但为什么抛出ConcurrentModificationException

如果检查ArrayList的代码。 首先执行范围检查,然后添加修改计数。

rangeCheck(index);
modCount++;

在范围检查方法中,范围检查仅适用于正数。

if (index >= size)
     throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

因此remove(0)不会添加mod计数,但remove(-1)会添加。 modCount导致迭代器抛出ConcurrentModificationException。

这是(IMO)一个错误,虽然是一个非常小的错误。

代码看起来像这样(在Java 8中):

 public E remove(int index) {
     rangeCheck(index);
     modCount++;
     E oldValue = elementData(index);

rangeCheck调用检查index < size ,但不rangeCheck index >= 0 据推测,这是为了避免冗余检查...因为elementData调用执行了一个不可优化的数组边界检查来处理负索引情况。

但问题是modCount++是在数组边界检查之前发生的。 哎呀!

一个潜在的解决方法是在elementData调用之后移动modCount++ ,但这可能不正确。 这个交易是modCount是一个volatile ,因此读/写它对访问下一个发生的elementData有一个隐含的同步效果。

如果你举报它们会修复它吗? 可能不是!

  • 修复它有可能破坏“工作”的代码,如果你按照我上面建议的方式这样做。 不可否认,你破坏的代码无论如何都是错误的,因为它依赖于因volatile而偶然发生的同步。 即便如此,代码破灭的人也会尖叫 (尤其是因为这将是一个难以诊断的破损。)

  • 另外,如果你添加一个明确的size >= 0检查,你最有可能做的工作代码可测量慢。

  • “bug”只会影响不正确的代码。 您不应该使用迭代同时调用ArrayList::remove

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM