开发中会遇到很多问题,比如:
public static void foreach(List<String> names) {
for (String s : names) {
names.remove(s);
}
}
会抛出 ConcurrentModificationException异常,但是下面这种写法:
public static void iterator(List<String> names) {
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
}
却不会抛出该异常!
我们看看这两个方法的字节码:
// access flags 0x9
// signature (Ljava/util/List<Ljava/lang/String;>;)V
// declaration: void foreach(java.util.List<java.lang.String>)
public static foreach(Ljava/util/List;)V
L0
LINENUMBER 13 L0
ALOAD 0
INVOKEINTERFACE java/util/
Ljava/util/Iterator;
ASTORE 1
L1
FRAME APPEND [java/util/Iterator]
ALOAD 1
INVOKEINTERFACE java/util/Iterator.hasNext ()Z
IFEQ L2
ALOAD 1
INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;
CHECKCAST java/lang/String
ASTORE 2
L3
LINENUMBER 14 L3
ALOAD 0
ALOAD 2
INVOKEINTERFACE java/util/List.remove (Ljava/lang/Object;)Z
POP
L4
LINENUMBER 15 L4
GOTO L1
L2
LINENUMBER 16 L2
FRAME CHOP 1
RETURN
L5
LOCALVARIABLE s Ljava/lang/String; L3 L4 2
LOCALVARIABLE names Ljava/util/List; L0 L5 0
// signature Ljava/util/List<Ljava/lang/String;>;
// declaration: java.util.List<java.lang.String>
MAXSTACK = 2
MAXLOCALS = 3
// access flags 0x9
// signature (Ljava/util/List<Ljava/lang/String;>;)V
// declaration: void iterator(java.util.List<java.lang.String>)
public static iterator(Ljava/util/List;)V
L0
LINENUMBER 19 L0
ALOAD 0
INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator;
ASTORE 1
L1
LINENUMBER 20 L1
FRAME APPEND [java/util/Iterator]
ALOAD 1
INVOKEINTERFACE java/util/Iterator.hasNext ()Z
IFEQ L2
L3
LINENUMBER 21 L3
ALOAD 1
INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;
POP
L4
LINENUMBER 22 L4
ALOAD 1
INVOKEINTERFACE java/util/Iterator.remove ()V
GOTO L1
L2
LINENUMBER 24 L2
FRAME SAME
RETURN
L5
LOCALVARIABLE names Ljava/util/List; L0 L5 0
// signature Ljava/util/List<Ljava/lang/String;>;
// declaration: java.util.List<java.lang.String>
LOCALVARIABLE iterator Ljava/util/Iterator; L1 L5 1
// signature Ljava/util/Iterator<Ljava/lang/String;>;
// declaration: java.util.Iterator<java.lang.String>
MAXSTACK = 1
MAXLOCALS = 2
我简化一下,把两个方法的字节码的关键部分贴出来:
foreach(List names):
Iterator.hasNext ()Z
Iterator.next ()
List.remove (Ljava/lang/Object;)Z
iterator(List names):
Iterator.hasNext ()Z
Iterator.next ()Ljava/lang/Object;
Iterator.remove ()V
我们先来看正确的iterator(List names)的调用:
Iterator.hasNext ()Z
Iterator.next ()Ljava/lang/Object;
Iterator.remove ()V
看看Iterator.next()和Iterator.remove的源码:
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];
}
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();
}
}
注意每次调用Iterator.next()和Iterator.remove()的时候都会调用:
checkForComodification()
而在该方法:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
大家可以看到一旦出现 modCount != expectedModCount 就会抛出异常!
并且最关键的是每次调用Iterator.remove的时候,都会有:
expectedModCount = modCount;
这就保证了每次调用checkForCommodification()时不会导致
expectedModCount != modCount的情况出现
那我们来说说foreach(List names)为什么导致异常抛出
foreach(List names)的关键字节码为:
Iterator.hasNext ()
Iterator.next ()
List.remove (Ljava/lang/Object;)Z
跟iterator(List names):
Iterator.hasNext ()Z
Iterator.next ()Ljava/lang/Object;
Iterator.remove ()V
不同的是 List.remove (Ljava/lang/Object;)Z
这个调用会直接调用:
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
}
注意这里的modCount++,但是并没有对expectedModCount++,这就导致了:
expectedModCount != modCount的情况出现
而在下一次Iterator.next()方法中调用会执行:
在Iterator.hasNext()方法中有如下调用:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这时异常发生了!
当我们显示的使用Iterator的时候,每次调用都会保证:
modCount = expectedModCount
所以不会发生问题,但是当我们使用增强的foreach循环的时候,我们如果直接调用 List.remove,就会破坏Iterator的循环逻辑,导致 modCount != expectedModCount,问题的根源就在这里,所以当我们使用增强的foreach循环的时候,不用List.remove,同样也不要调用List.add操作!