ArrayList源码分析(二)
今天来进行ArrayList中关于迭代器方面的相关分析。
Iterator
初始化以及内部属性详情
public Iterator<E> iterator() { return new Itr(); } /** * An optimized version of AbstractList.Itr */ 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 int expectedModCount = modCount; // prevent creating a synthetic constructor Itr() {} //其它代码 }
关于迭代器,可以从以上代码看出其内部并没有定义实际的数据之类的,只定义了几种方法,如cursor,lastRet,expectedModCount,用来提供迭代ArrayList对象的机制。
next方法
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]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); //当被并发修改时抛出异常 }
next 方法是实际用来迭代的方法,返回迭代的元素,并且让指针指向下一个元素。在进行一开始的检测后(如上注释所示),“if (i >= size)” 用来判断是否迭代完成,之后的“if (i >= elementData.length)”,因为到这里的前提是i < size,若满足i >= elementData.length这个条件说明size > elementData.length,抛出并发修改异常。之后cursor+1,将之前的cursor值赋给lastRet。
remove方法
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
forEachRemaining方法
public void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); final int size = ArrayList.this.size; int i = cursor; if (i < size) { final Object[] es = elementData; if (i >= es.length) throw new ConcurrentModificationException(); for (; i < size && modCount == expectedModCount; i++) action.accept(elementAt(es, i)); // update once at end to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } }
关于此部分代码,关键还是在for循环这块,一开始将cursor赋给i,故此循环的范围为cursor到size-1,循环中进行的操作为action.accept(elementAt(es, i)),我们可以点accept进去看下,其为Consumer函数式接口中的一个抽象方法。
我们可以这样调用 forEachRemaining :iter.forEachRemaining(System.out::println);,这样就可以快速的打印iter里剩下的元素,其实是用方法引用来代替实现了Consumer接口的类的对象,也可以自行编写lambda表达式,当然,也可以定义一个Consumer接口的实现类。
ListIterator
初始化以及内部属性详情
private class ListItr extends Itr implements ListIterator<E> { ListItr(int index) { super(); cursor = index; } public ListIterator<E> listIterator(int index) { rangeCheckForAdd(index); return new ListItr(index); } public ListIterator<E> listIterator() { return new ListItr(0); }
如上所示,ListIterator根据指定的index进行迭代。
previous方法
public E previous() { checkForComodification(); int i = cursor - 1; if (i < 0) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i; return (E) elementData[lastRet = i]; }
跟next方法大部分代码相同,本方法返回的是cursor指向元素的前一个元素,正如其名。
set方法
public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.set(lastRet, e); } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
set方法是用来进行替换操作的方法,要求lastRet >= 0,当然也有并发修改的相关的检测。
add方法
public void add(E e) { checkForComodification(); try { int i = cursor; ArrayList.this.add(i, e); cursor = i + 1; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
调用ArrayList自己的add之后,会让cursor自增一个,由于add 会把index之后的元素都向后挪一位,所以cursor还是指向它之前指向的元素,最后设置expectedModCount。
SubList
SubList的执行结果是获取ArrayList的一部分,返回的是ArrayList的部分视图。
初始化以及内部属性详情
public List<E> subList(int fromIndex, int toIndex) {
//fromIndex---截取元素的起始位置,包含该索引位置元素
//toIndex-----截取元素的结束位置,不包含该索引位置元素 subListRangeCheck(fromIndex, toIndex, size); return new SubList<>(this, fromIndex, toIndex); } private static class SubList<E> extends AbstractList<E> implements RandomAccess { private final ArrayList<E> root; private final SubList<E> parent; private final int offset; private int size; /** * 从ArrayList创建SubList. */ public SubList(ArrayList<E> root, int fromIndex, int toIndex) { this.root = root; this.parent = null; this.offset = fromIndex; this.size = toIndex - fromIndex; this.modCount = root.modCount; } /** * 从SubList再创建SubList. */ private SubList(SubList<E> parent, int fromIndex, int toIndex) { this.root = parent.root; this.parent = parent; this.offset = parent.offset + fromIndex; this.size = toIndex - fromIndex; this.modCount = parent.modCount; }
由于SubList继承了AbstractList类,所以它自己也有一个modCount属性;可以选择由ArrayList或者subList来创建一个新的subList。首先看由ArrayList初始化subList,毕竟你需要有第一个subList,才能用subList初始化出其它的subList。可以看到,这里把root 设置为这个ArrayList对象本身,parent设置为null,然后分别设置offset,size和modCount。
再看由SubList初始化SubList,可以发现,root还是那个root,不管你subList嵌套了多少层,parent就是此subList上面一层,offset就是此subList相对于原始ArrayList的偏移量,层层叠加,size就是subList的长度,modCount和parent的保持一致。
get方法
public E get(int index) { Objects.checkIndex(index, size); checkForComodification(); return root.elementData(offset + index); }
get方法较为简单,首先还是老样子,进行相关检测,如同步检测,越界检测,然后以offset + index作为索引的下标返回,offset相当于偏移量。
add方法
public void add(int index, E element) { rangeCheckForAdd(index); checkForComodification(); root.add(offset + index, element); updateSizeAndModCount(1); }
private void updateSizeAndModCount(int sizeChange) {
SubList<E> slist = this;
//改变此层及以上的size和modCount
do {
slist.size += sizeChange;
slist.modCount = root.modCount;
slist = slist.parent;
} while (slist != null);
}
该方法主要还是靠调用root的add即ArrayList本身的add的方法,主要还是看最后的updateSizeAndModCount(1),递归的改变这层和以上层的size和modCount,从这也可以发现,此层以下的subList就不管了,所以如果subList嵌套了许多层,需要用subList进行结构性修改的话,最好用最下面那层来改,不然,下面的subList就都废掉了。
还有其他方法,但大都大同小异,就不具体分析了。最后关于迭代器中的for-each循环, 其实就是新建了一个迭代器,不断进行hasNext()
和next()
的调用。
就这样吧,这些源码分析主要还是自用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)