1. java.util.Iterator接口
在Java中Iterator为一个接口,它只提供了迭代了基本规则,在JDK中他是这样定义的:对 collection 进行迭代的迭代器。迭代器取代了 Java Collections Framework 中的 Enumeration。迭代器与枚举有两点不同:
1、迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的 collection 移除元素。
2、方法名称得到了改进。
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
}
其中:
Object next():返回迭代器刚越过的元素的引用,返回值是Object,需要强制转换成自己需要的类型
boolean hasNext():判断容器内是否还有可供访问的元素
void remove():删除迭代器刚越过的元素
迭代其实我们可以简单地理解为遍历,是一个标准化遍历各类容器里面的所有对象的方法类,它是一个很典型的设计模式(迭代子模式)。Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。 在没有迭代器时我们都是这么进行处理的。
前面阐述了Iterator有一个很大的优点,就是我们不必知道集合的内部结果,集合的内部结构、状态由Iterator来维持,通过统一的方法hasNext()、next()来判断、获取下一个元素,至于具体的内部实现我们就不用关心了。但是作为一个合格的程序员我们非常有必要来弄清楚Iterator的实现。下面就ArrayList的源码进行分析分析。
* 二、各个集合对Iterator接口的实现 *
下面就ArrayList的Iterator实现来分析,其实如果我们理解了ArrayList、Hashset、TreeSet的数据结构,内部实现,对于他们是如何实现Iterator也会胸有成竹的。因为ArrayList的内部实现采用数组,所以我们只需要记录相应位置的索引即可,其方法的实现比较简单。
2.1、ArrayList的Iterator实现
在ArrayList内部首先是定义一个内部类Itr,该内部类实现Iterator接口,如下:
01.private class Itr implements Iterator<E> {
02. //do something
03.}
在Itr内部定义了三个int型的变量:cursor、lastRet、expectedModCount。其中cursor表示下一个元素的索引位置,lastRet表示上一个元素的索引位置
01.int cursor;
02.int lastRet = -1;
03.int expectedModCount = modCount;
从cursor、lastRet定义可以看出,lastRet一直比cursor少一所以hasNext()实现方法异常简单,只需要判断cursor和size(存储实际元素的大小)是否相等即可。
public boolean hasNext() {
return cursor != size;
}
对于next()实现其实也是比较简单的,只要返回cursor索引位置处的元素即可,然后修改cursor、lastRet即可,
public E next() {
checkForComodification(); //判断ArrayList集合的modCount和Iterator的expectedModCount是否相等
int i = cursor; //记录索引位置
if (i >= size) //如果获取元素大于集合元素个数,则抛出异常
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) //当前索引大于数组的长度抛出异常
throw new ConcurrentModificationException();
cursor = i + 1; //游标位置+1
return (E) elementData[lastRet = i]; //返回上一个索引处的值。
}
checkForComodification()主要用来判断集合的修改次数是否合法,即用来判断遍历过程中集合是否被修改过。在java提高篇(二一)-----ArrayList中已经阐述了。modCount用于记录ArrayList集合的修改次数,初始化为0,,每当集合被修改一次(结构上面的修改,内部update不算),如add、remove等方法,modCount + 1,所以如果modCount不变,则表示集合内容没有被修改。该机制主要是用于实现ArrayList集合的快速失败机制,在Java的集合中,较大一部分集合是存在快速失败机制的,这里就不多说,后面会讲到。所以要保证在遍历过程中不出错误,我们就应该保证在遍历过程中不会对集合产生结构上的修改(当然remove方法除外),出现了异常错误,我们就应该认真检查程序是否出错而不是catch后不做处理。
final void checkForComodification() {
02. if (modCount != expectedModCount)
03. throw new ConcurrentModificationException();
04. }
对于remove()方法的是实现,它是调用ArrayList本身的remove()方法删除lastRet位置元素,然后修改modCount即可。
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); //调用ArrayList的remove方法删除当前元素,为什么参数是lastRet这跟next()方法的源码有关,next中拿到当前元素的同时,cursor游标已经指向了下一个元素的位置。
cursor = lastRet;
lastRet = -1; //设置lastRet为-1使remove方法不能连续被调用。
expectedModCount = modCount; //设置修改次数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
ArrayList的remove()方法的实现。
public E remove(int index) {
rangeCheck(index); //检查index是否越界
modCount++; //修改次数+1
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,把删除元素那个位置向的值设为null
return oldValue; //返回删除元素的值
}