当我们移除-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
被抛出?
答案 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
,但这可能不正确。该交易是modCount
是volatile
,因此读/写它对访问接下来发生的elementData
具有隐含的同步效果。
如果你举报它会修复它吗?可能不是!
修复它有可能破坏“工作”的代码,如果按照我上面的建议进行操作。不可否认,您破解的代码无论如何都是错误的,因为它依赖于由volatile
引起的偶然同步。即便如此,代码破坏的人也会尖叫。 (尤其是因为这将是一个难以诊断的破损。)
或者,如果您添加明确的size >= 0
检查,则很可能会使代码可测量变慢。
“bug”只会影响不正确的代码。您不应该同时使用迭代调用ArrayList::remove
。