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

时间:2017-09-27 07:18:12

标签: java arraylist

当我们移除-1并清空ArrayList时,它会抛出ConcurrentModificationException,当我们从同一个空0移除ArrayList时,它会抛出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被抛出?

2 个答案:

答案 0 :(得分:4)

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

rangeCheck(index);
modCount++;

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

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

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

答案 1 :(得分:3)

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

代码如下所示(在Java 8中):

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

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

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

可能的解决方法是在modCount++电话后移动elementData,但这可能不正确。该交易是modCountvolatile,因此读/写它对访问接下来发生的elementData具有隐含的同步效果。

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

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

  • 或者,如果您添加明确的size >= 0检查,则很可能会使代码可测量变慢。

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