对JAVA集合进行遍历删除时务必要用迭代器
0.情景引出
对新手来说,遍历一个集合时难免会碰到删除操作,直接删除的话,肯定是要抛异常的
1 public static void main(String[] args) { 2 3 List<String> list = new ArrayList<>(); 4 for(int i=0;i<10;i++){ 5 list.add(String.valueOf(i)); 6 } 7 System.out.println(list.toString()); 8 System.out.println("----------------------"); 9 for (String str:list){ 10 if(str.equals("5")) 11 list.remove(str); 12 } 13 //会抛出异常java.util.ConcurrentModificationException 14 15 }
异常的文档说明:
所以呢,聪明的你,大概可以找解决方案了,我以前呢倒是会建一个临时的集合C2和需要删减的集合C1一致,一个循环,一个删减,对于迭代器好像都是敬而远之!!
1.问题解决
咳咳,这里呢,就是为了让自己明白点
1 public static void main(String[] args) { 2 3 List<String> list = new ArrayList<>(); 4 for(int i=0;i<10;i++){ 5 list.add(String.valueOf(i)); 6 } 7 System.out.println(list.toString()); 8 9 System.out.println("----------------------"); 10 11 Iterator iter = list.iterator(); 12 while (iter.hasNext()){ 13 String str = (String) iter.next(); 14 if(str.equals("5")) 15 iter.remove(); 16 //错误的写法,list.remove(str);,这种类似于上面的 17 } 18 19 System.out.println(list); 20 21 }
文章到此应该结束了。下面只是补充下,我自己对Iterator的理解
2.扩展Iterator
2.1 我们先来看一下Iterator的api解释:
这个移除元素的特点看来是与生俱来的吧,
2.2 我们再看一下ArrayList中的Iterator实现:
1 private class Itr implements Iterator<E> { 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 or 8 * previous. Reset to -1 if this element is deleted by a call 9 * to remove. 10 */ 11 int lastRet = -1; 12 /** 13 * The modCount value that the iterator believes that the backing 14 * List should have. If this expectation is violated, the iterator 15 * has detected concurrent modification. 16 */ 17 int expectedModCount = modCount; 18 public boolean hasNext() { 19 return cursor != size(); 20 } 21 public E next() { 22 checkForComodification(); 23 try { 24 E next = get(cursor); 25 lastRet = cursor++; 26 return next; 27 } catch (IndexOutOfBoundsException e) { 28 checkForComodification(); 29 throw new NoSuchElementException(); 30 } 31 } 32 public void remove() { 33 if (lastRet == -1) 34 throw new IllegalStateException(); 35 checkForComodification(); 36 try { 37 AbstractList.this.remove(lastRet); 38 if (lastRet < cursor) 39 cursor--; 40 lastRet = -1; 41 expectedModCount = modCount; 42 } catch (IndexOutOfBoundsException e) { 43 throw new ConcurrentModificationException(); 44 } 45 } 46 final void checkForComodification() { 47 if (modCount != expectedModCount) 48 throw new ConcurrentModificationException(); 49 } 50 }
Iterator采用cursor来来维护自已的状态,而上ArrayList采用size属性来维护自已的状态
当size出现变化时,cursor并不一定能够得到同步,除非这种变化是Iterator主动导致的。
从上面的代码可以看到当Iterator.remove方法导致ArrayList列表发生变化时,他会更新cursor来同步这一变化。但其他方式导致的ArrayList变化,Iterator是无法感知的。ArrayList自然也不会主动通知Iterator们,那将是一个繁重的工作。Iterator到底还是做了努力:为了防止状态不一致可能引发的无法设想的后果,Iterator会经常做checkForComodification检查,以防有变。如果有变,则以异常抛出,所以就出现了上面的异常。
我只想安静的学习,捡拾前人的牙慧。默默地强大如此弱小的我...