最近研读了阿里巴巴Java编程规范,通过学习前辈踩过的坑,发现自己和编程规范的要求大部分是相同的,这个编程规范应该得到推广,好的编程习惯从一个好的命名开始
看到以下的这条规范,自己试验后发现确实以前没有主意到这个细节,于是自己开始了详细的分析,折腾了好久好久,天都黑了
package com.copycat.test04;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class AlibabaJavaCodeGuidLine
{
public static void test01()
{
List<String>list=new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
/*
for(String item : list)
{
System.out.println("++++++++"+item);
if("6".equals(item)) //把这里的"1"换成"2","3","4","5","6","7"后再执行
{
list.remove(item);
//break; //如果加上break后则所有的执行都是正确的
System.out.println("remove the "+item);
}
}*/
Iterator<String> iterator = list.iterator(); //为了更好的跟踪程序的运行过程
while(iterator.hasNext()) //我将上面的增强for循环改为这里的iterator来实现,本质和
{ //上面的循环是一样的
String string=iterator.next(); //可以发现remove被执行后modCount和expectedModCount的值不一样
if("6".equals(string)) //导致next()函数中的checkForComodification()函数返回false,抛出异常
{ //这时后程序抛出java.util.ConcurrentModificationException,然后终止程序
list.remove(string);
}
}
for(String it:list)
{
System.out.println(it);
}
}
public static void main(String[] args)
{
test01();
}
}
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.copycat.test04.AlibabaJavaCodeGuidLine.test01(AlibabaJavaCodeGuidLine.java:17)
at com.copycat.test04.AlibabaJavaCodeGuidLine.main(AlibabaJavaCodeGuidLine.java:34)
通过抛出的异常分析是并发修改里面的问题,于是要分析一下源代码里的详细细节
ArrayList的源码里面有一个内部类是迭代器
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
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();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
看一下有几个重要的成员变量
cursor:表示下一个要访问的元素的索引,从next()方法的具体实现就可看出
lastRet:表示上一个访问的元素的索引
expectedModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount。
modCount是AbstractList类中的一个成员变量
给出程序的过程图解,请内心观察,插入7个元素后的modCount和expectedModCount的值是一样的
然后一直取元素做判断,直到remove()被执行,这个过程主意黄色的变化
到这时要执行remove操作,注意modCount和size的变化
这时hasNext(){
return cursor!=size()}返回false不再执行了,造成后面的元素不再判断了,因此只有倒数第二个元素("6".equal())去比较的时候才会运行正常(实际是巧合),
其它元素去比较都
会在Next()函数中抛出异常,原因是cursor和size的值不同返回true,然后遇到next()中的checkForComodification()而抛出异常
总结:不要在foreach循环里进行元素的remove/add操作,remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁
Iterator中expectedModCount = modCount;避免了在next()中checkForComdification()抛出异常