先看一段代码
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
Iterator<Integer> it = list.iterator();
Collections.sort(list);
while (it.hasNext()) {
System.out.println(it.next());
}
Java7 运行效果
1
2
3
4
5
Java8 运行效果 (异常ConcurrentModificationException)
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)
结果分析
在上面的代码中,我们先得到list
的iterator
,然后对list
进行排序,最后遍历iterator
。
从Java8的错误信息中可以看出it.next( )
方法中检查list是否已经被修改,由于在遍历之前进行了一次排序,所以checkForComodification
方法抛出异常ConcurrentModificationException
。
这个可以理解,因为排序嘛,肯定会修改list...
但是为啥Java7中没有这个问题呢???
源码分析
首先看checkForComodification
方法是如何判断的,如下所示:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
可以看出,有两个内部成员变量用来判断是否发生了修改: modCount
和 expectedModCount
。
Iterator中记录了expectedModCount
,List中记录了modCount
。checkForComodification
方法通过比较modCount
和 expectedModCount
来判断是否发生了修改。
在Java7中,Collections.sort( list )调用的是Collections自身的sort方法,如下所示:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Object[] a = list.toArray();
Arrays.sort(a);
ListIterator<T> i = list.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((T)a[j]);
}
}
可以看出,该排序算法只是改变了List中元素的值(i.set((T)a[j]);
),并没有修改modCount
字段。所以checkForComodification
方法不会抛出异常。
而在Java8中,Collections.sort( list )调用的是ArrayList自身的sort方法,如下所示:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
ArrayList的sort方法实现如下:
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
可以看出最后一行,modCount++;
,修改了modCount
字段。所以checkForComodification
方法会抛出异常。
关于Java8中Collections.sort方法的修改,可以参见官方描述:
Previously Collection.sort copied the elements of the list to sort into an array, sorted that array, then updated list, in place, with those elements in the array, and the default method List.sort deferred to Collection.sort. This was a non-optimal arrangement.
From 8u20 release onwards Collection.sort defers to List.sort. This means, for example, existing code that calls Collection.sort with an instance of ArrayList will now use the optimal sort implemented by ArrayList.