这篇博文主要记录下面几个问题:
1、for循环遍历能不能删除数据不报错。
普通for循环(for(int i=0;i<list.size;i++)) 可以;加强型不可以(for(String str:list))原因下面会解释
2、迭代器能不能删除数据。
可以(如果你没有使用错误的话)错误的情况下面也会解释
首先看for循环删除集合元素的情况:
1、普通for循环删除元素:
List<String> list = new ArrayList<String>();
list.add("one");
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add("six");
for(int i=0;i<list.size();i++){
String temp = list.get(i);
if(temp.equals("one")){
list.remove(i);
}
}
System.out.println(list);
这段代码运行是不会报异常的,但是打印的内容有点问题,只删除了一个“one”还有一个“one”,导致的原因就是i++; 因为删除第一个元素“one”的时候,第二个元素“one”变成了第一个元素,也就是整体向前移动一位,删除源码:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
也就是index=0了,而此时for循环里i++,i=1了,这是就遍历不到index=0的这个位置了,所以删除不了,但是稍微修改下还是可以删除的,代码做如下调整:
List<String> list = new ArrayList<String>();
list.add("one");
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add("six");
for(int i=0;i<list.size();i++){
String temp = list.get(i);
if(temp.equals("one")){
list.remove(i);
i--;
}
}
System.out.println(list);
满足条件时添加i--;将下标回退下,就可以遍历到每个位置了;
2、加强型for循环删除元素:
List<String> list = new ArrayList<String>();
list.add("one");
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add("six");
for(String ss:list){
if(ss.equals("one")){
list.remove(ss);
}
}
System.out.println(list);
这个代码是会报异常的:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.auto.zjl.thread.Test.main(Test.java:36)
原因下面再说,接着看用迭代器的操作:
List<String> list = new ArrayList<String>();
list.add("one");
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add("six");
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
if(str.equals("one")){
//1:迭代器删除 it.remove();
//2:列表删除 list.remove(str);
}
}
System.out.println(list);
上面的两种删除方式 1:迭代器删除是可以成功的;2:列表删除是失败的,会报上面的那个异常
下面说说原因:
1、加强型for循环用的也是迭代器
2、迭代器的删除方法是有更新modCount的,源码:
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();
}
}
可以看到这条语句
expectedModCount = modCount;
而上面那个异常则是 对这两个值的比较,不同的时候报的异常:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这里的迭代器用的也是list的remove:语句:ArrayList.this.remove(lastRet);源码:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
可以看到 modCount++; 即每次都会修改这个值;现在回过头来看下:迭代器删除的那两种方式
//1:迭代器删除 it.remove();
//2:列表删除 list.remove(str);
观察下上面的两个方法的源码就会发发现,迭代器的删除方法是有更新要比较的值的:expectedModCount = modCount;
而集合的删除是没有同步内部迭代器的值的;所以在使用迭代器删除的时候,误用了集合的删除方法是会报异常的,而加强型for循环用的就是迭代器,但是删除又只能用list.remove();所以是不行的;
但为什么每次修改都有记录modCount呢:ArrayList是非线程安全的,在操作过程中如果有别的线程修改集合内容,那集合数据可能就不准确了,这时候抛出异常避免继续执行可以看做是个快速失败的方法(个人理解)