这个为什么要单独说的原因是,在开发中的对数据库中的增删为最基本的,但是是不是写对了就尤为重要
先来看代码:
1 public voidtestLoopInList(){2 List a = new ArrayList();3 a.add("1");4 a.add("2");5 a.add("w");6 for(String string : a) {7 System.out.println(string);8 }9
10 for(String temp : a) {11 if("2".equals(temp)){12 a.remove(temp);13 }14 }15
16 for(String string : a) {17 System.out.println(string);18 }
输出:
1
2java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at test.Test_ForeachAndIterator.testLoopInList(Test_ForeachAndIterator.java:19)
出现错误了
原因:首先从错误中可以看出,是ArraList中的Iterator的checkForComodification()出现的错误,说明了foreach的实现原理其实就是实现了内部类I特Iterator来进行遍历的,其次为什么会出错呢?
因为在ArrayList和ArrayList的内部类的Iterator中的都会存在remove的方法,而ArrayList和Iterator中都各自有自己的维持长度的变量,前者是modCount,后者是expectModCount,但是源码中ArrayList的remove方法是会改变modCount的值,但却不会直接同步到expectModCount的值的,而Iterator中时刻都插入了checkForComodification()方法来监测modCount是否与expectModCount相等,所以就会很容易出现异常错误,当然下面的代码也是错的
1 public voidtestLoopInList(){2 List a = new ArrayList();3 a.add("1");4 a.add("2");5
6 for(String string : a) {7 System.out.println(string);8 }9
10 Iterator it =a.iterator();11 while(it.hasNext()){12 if("1".equals(it.next())){13 a.remove(it.next());14 }15 }16
17 for(String string : a) {18 System.out.println(string);19 }20 }
错误原因同上
所以在解决问题的关键就是要避免这个异常的出现,也就是时刻让modCount==expectModCount,所以就是使用iterator的remove方法,因为会有使两者相等的代码
即下面:
1 public voidtestLoopInList(){2 List a = new ArrayList();3 a.add("1");4 a.add("2");5
6 for(String string : a) {7 System.out.println(string);8 }9
10 Iterator it =a.iterator();11 while(it.hasNext()){12 if("1".equals(it.next())){13 it.remove();14 }15 }16
17 for(String string : a) {18 System.out.println(string);19 }20 }
相关源码提上:
1 private class Itr implements Iterator{2 /**
3 * Index of element to be returned by subsequent call to next.4 */
5 int cursor = 0;6 /**
7 * Index of element returned by most recent call to next or8 * previous. Reset to -1 if this element is deleted by a call9 * to remove.10 */
11 int lastRet = -1;12 /**
13 * The modCount value that the iterator believes that the backing14 * List should have. If this expectation is violated, the iterator15 * has detected concurrent modification.16 */
17 int expectedModCount =modCount;18 public booleanhasNext() {19 return cursor !=size();20 }21 publicE next() {22 checkForComodification();23 try{24 E next =get(cursor);25 lastRet = cursor++;26 returnnext;27 } catch(IndexOutOfBoundsException e) {28 checkForComodification();29 throw newNoSuchElementException();30 }31 }32 public voidremove() {33 if (lastRet == -1)34 throw newIllegalStateException();35 checkForComodification();36 try{37 AbstractList.this.remove(lastRet);38 if (lastRet
ArrayList中的remove
1 public booleanremove(Object o) {2 if (o == null) {3 for (int index = 0; index < size; index++)4 if (elementData[index] == null) {5 fastRemove(index);6 return true;7 }8 } else{9 for (int index = 0; index < size; index++)10 if(o.equals(elementData[index])) {11 fastRemove(index);12 return true;13 }14 }15 return false;16 }
当然你说我不用for增强,就用普通的for ,就不会有创建Iterator的操作,进而就不会有Iterator的实时监测维护值得操作,也行啊,但还是要注意有坑啊! 比如我们来看下面的代码:
1 List list = newArrayList();2 Collections.addAll(list, 1, 2, 3, 4, 5);3 for(int i = 0; i < list.size(); ++i){4 int val = (int) list.get(i);5
6 //需求是删除 3 和 4 两个元素
7 if(3 == val || 4 ==val){8 list.remove(i);9 }10 }
这是我们非常常见的写法,结果 为 :1 2 3 4
奇怪了,4为甚没有删除掉,其实仔细想想 还是能相同的 删除了3之后 i = 3 size = 4,这时候,list.get返回值为5, 会跳过4
所以 修改的话,两个 liter tips : 要么自己维护索引,要么反向循环
1 for(int i = list.size() - 1; i > 0; --i){2 int val = (int) list.get(i);3
4 //需求是删除 3 和 4 两个元素
5 if(3 == val || 4 ==val){6 list.remove(i);7 }8 }
1 for(int i = 0; i < list.size(); ++i){2 int val = (int) list.get(i);3
4 //需求是删除 3 和 4 两个元素
5 if(3 == val || 4 ==val){6 list.remove(i);7 i --; //删除后,索引也应该变化
8 }9 }
ok, that's all ...