java提供了一种遍历数据的方法,即用迭代器。虽然迭代器是可以遍历数据的,但是本质上并不是一种方法,而是一种设计模式。迭代器通常被称为是一种“轻量级”的对象
注意:iterator()方法是java.lang.Iterable接口下的一个方法,Collection也继承这个接口。 iterator中有三个方法:
package java.util; public interface Iterator<E> { boolean hasNext();//判断是否存在下一个对象元素 E next();//获取下一个元素 void remove();//移除元素 }1.第一次调用.next()方法的时候,返回的是序列中的第一个元素。再次使用.next()方法时既可以使指针向后移一位又能将当前元素返回。 2.使用.hasNext()是判断序列中是否还存在下一个元素。如果存在,会判断为true执行.next()方法返回这个元素。依次类推
此处需要用entrySet方法,entrySet是把一个整体(key-value)作为一个整体一对一的存放到set集合当中。
如果使用这种方式进行一边遍历一边删除的话运行的时候会发现报错了。 这个异常代表并发修改异常,为什么会抛出这个异常呢。因为元素在使用的时候发生了并发的修改,导致异常抛出。但是如果在删除一个元素之后马上使用break跳出,则不会触发报错,因此这种方式只适用于只删除一个元素的场景。通过查看源代码可以发现,报错的原因在于在checkForComodification()方法中如果modCount变量不等于expectedModCount变量,就会抛出ConcurrentModificationException异常。 下面放图解释一下: forEach在循环执行的时候,其实使用的是iterator,里面最主要的方法就是hasNext()和next().知道了这些东西之后,我们去看看ArrayList当中是否有iterator这个方法。经过查找并没有找到,很显然应该在他的父类AbstractList当中。
//通过这段代码发现引用了Itr这个方法,在继续找到他的实现 public Iterator<E> iterator() { return new Itr(); } //找到了Itr也在AbstractList类当中 private class Itr implements Iterator<E> { /** * Index of element to be returned by subsequent call to next. */ int cursor = 0; //表示下一个要访问元素的索引 /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove. */ int lastRet = -1; //表示上一个访问元素的索引 /** * The modCount value that the iterator believes that the backing * List should have. If this expectation is violated, the iterator * has detected concurrent modification. */ int expectedModCount = modCount; //expectedModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount。 //modCount是AbstractList类中的一个成员变量 public boolean hasNext() { return cursor != size(); } public E next() { checkForComodification(); try { int i = cursor; E next = get(i); lastRet = i; cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }上面的代码中有几个重要的变量,分别解释一下他们的含义: cursor:初始值是0,代表下一个要访问元素的索引 lastRet :初始值是-1,代表上一个要访问元素的索引 int expectedModCount:代表对ArrayList修改的一个期望值,初始值是modCount modCount:是已经定义好的变量 modCount表示对list修改的次数,通过查看ArrayList中的add()方法和remove()方法发现会对modCount进行+1; 接下来我们继续来分析List使用迭代器的一些代码,先来看一下hasNext方法
public boolean hasNext() { return cursor != size(); }当使用迭代器遍历时,通过这个方法来判断是否还有下一个元素。如果下一个元素不等与ArrayList元素的大小,就代表有元素需要访问。如果下一个元素等于ArrayList的大小就代表肯定到达末尾了。
public E next() { checkForComodification(); try { int i = cursor; E next = get(i); lastRet = i; cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } }这是最主要的地方:当执行这个方法时,首先会走checkForComodification();这个方法,初始化的时候 cursor=0,lastRet=-1,当调用了一次之后,cursor变为1,lastRet变为0。此时,modCount为0,expectedModCount也为0。
写的程序上面,当遍历到等于“小猫”的时候,就会调用list的remove方法。此时我们来看一下remove方法并解释一下。
public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } /* * Private remove method that skips bounds checking and does not * return the value removed. */ private void fastRemove(int index) { modCount++; 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 }以上代码可以看到,当执行remove删除元素时,最终都会调用fastRemove方法,首先对modCount进行加一操作,原因时对集合进行了修改,然后下面就是删除元素的操作,最后将size进行减一操作。
!!!此时我们来梳理一下现在的各个变量:对于iterator来说,在调用next方法中,expectedModCount这个变量并没有改变,因此还为0。cursor变为1,lastRet变为0。(上边已经提到) 对于list来说,执行完remove会对modCount+1,所以modCount=1,size=2(代码中添加了三个元素,删除一个=2) 现在我们继续执行程序,会调用hasNext()进行判断,此时cursor=1,size=2,返回为true,会继续执行next()方法。此时注意next方法的第一行代码发现; 很显然,经过我们以上的分析,modCount=1,expectedModCount=0.因此程序就抛出了ConcurrentModificationException异常。