前言
最近找工作,遇到很多底层的问题,于是决定把Java中常用的类、方法等的底层好好看看,再学学。在学到迭代器时,在hasNext()与next()的初始指针的指向问题上出现了分歧,有说起始指针为0的,也有说为-1的。然后去看了源码,自认为是懂了一点,于是记录一下,如果有错误、不严谨的地方,望路过的大佬能批评指正。
理解过程
首先,创建一个list,然后获取迭代器对象,完成他的迭代器遍历的流程
// 1、创建集合并添加元素
Collection<String> coll = new ArrayList();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
// 2、获取迭代器对象
Iterator<String> it = coll.iterator();
// 3、利用循环不断去获取集合中的每个元素
while(it.hasNext()){
// 获取元素并移动指针
String str = it.next();
System.out.println(str);
}
在这个过程中,coll.iterator()在底层实际是返回了一个Itr对象,也就是说我们使用的迭代器的方法都是来自于这个Itr类的
public Iterator<E> iterator() {
return new Itr();
}
下面就是Itr类部分方法,在hasNext()与next()两个方法中,我们只需要关注前两个成员变量(cursor和lastRet),第三个变量是关于迭代器遍历中增删限制的,这里不做讨论。
cursor,英文翻译就是指针、游标、光标;源码注释为下一个返回元素的索引;初始化为0。lastRet,根据源码注释,解释为返回的最后一个元素的索引,如果没有就为-1;初始化为-1。
先看hasNext()方法,只做了cursor与size的比较,并没有做指针的移动等操作。
然后是next(),checkForComodification()是与第三个参数有关,目前不管他。
先把目前的游标用一个局部变量 i 存储,然后比较 i 是否越界(集合长度),再把底层数组用局部变量的数组存储(可以提高效率),然后比较 i 是否越界(数组长度),再然后移动游标(i+1,本质上也就是cursor+1),最后利用 lastRet 去取数组中的元素。注意,此时的 lastRet 是比 cursor 少1的。
private class Itr implements Iterator<E> {
int cursor; // index of next element to return --> 下一个返回元素的索引
int lastRet = -1; // index of last element returned; -1 if no such --> 返回最后一个元素的索引;如果没有-1
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
}
结论
1、 在初学hasNext()方法时,遇到的每个人(同学、老师...)对其的描述都是:查看他是否有下一个元素。这种描述就让我以为该方法是在查看下一个地址上是否有元素,然而源码中做的操作是,比较目前游标是否达到了集合的实际长度,也就是在比较游标是否越界。
2、next()中其实涉及了两个指针,cursor与lastRet。cursor起始为0,lastRet起始为-1。那哪一个才是指针的起始位呢?我们可以反复琢磨一下他的源码注释。cursor:下一个返回元素的索引;lastRet:返回的最后一个元素的索引。再结合elementData[lastRet = i]这句来看。实际就可以看成指针先移动到下一个位置,再取之前位置上的元素。所以我个人认为,起始位是0。
3、lastRet主要是用来记录遍历到的最新一个元素的位置的,而真正移动的指针是cursor,lastRet的值也是由 cursor 来的,所以起始位为0应该是最合理的解释。