ArrayList源码解析(四)
这篇文章主要看ArrayList的Iterator和ListIterator的实现。
1.Iterator和类Itr
当我们调用iterator方法时返回一个Iterator。
/** * Returns an iterator over the elements in this list in proper sequence. * * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>. * * @return an iterator over the elements in this list in proper sequence */ public Iterator<E> iterator() { return new Itr(); }
而iterator()方法返回的Iterator是由一个私有类向上转型得到的:
/** * 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; 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]; } public void remove() { if (lastRet < 0) //lastRet初值为-1,在没有调用过next()方法保持为-1,不是任何元素的索引;调用一次remove()方法之后为-1,也就是说不能连续调用remove()方法 throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; //要移除的最近返回的元素,lastRet指向最近返回的元素,而cursor指向下一个要返回的元素,移除了一个元素,自然指向lastRet位置
lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); //对元素执行指定的操作,遍历i之后的所有元素 } // update once at end of iteration to reduce heap write traffic cursor = i; //退出上面的while循环时,i等于size lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
Itr()里定义了三个变量:
int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount;
cursor 下一个将要返回的元素的索引;
lastRet 最近(上一个)返回的元素的索引,-1表示最近的一次操作没有返回元素。
expectedModCount前面提到过。
Itr类实现了next()、hasNext()、remove()和forEachRemaining(Consumer<? super E> consumer)方法:
hasNext()
public boolean hasNext() { return cursor != size; }
在next()返回的是cursor指向的元素,所以它不为size时表示还有下一个元素,即调用next()还可以返回元素。
next()
@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]; }
checkForComodification()
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
用来检查modCount是否等于expectedModCount,前文提到过,这里不再赘述。
next()
@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]; }
主要就是返回cursor指向的元素,然后cursor加1,并令lastRet=i,使其指向最近返回的元素。
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(); } }
Remove使用了lastRet的值。
注意这里是调用ArrayList的remove实现移除元素的,并使cursor=lastRet,因为移除了一个元素,调用next()返回的元素就位于lastRet了。
lastRet赋值为-1,因为这次操作没有返回元素。
因为修改了elementData的结构,所以需要expectedModCount=modCount。
forEachRemaining(Consumer<? super E> consumer)
@Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); }
注意@Override注解,表明这覆盖了父接口的方法。
这个方法就是对从cursor指向的元素开始到集合的最后一个元素,调用consumer.accept()方法进行指定的处理。
调用完这个方法后,cursor值为size,指向所有元素之后,lastRet指向了集合的最后一个元素。
2.ListIterator和类ListItr
当我们调用listIterator()方法时返回一个ListIterator:
/** * Returns a list iterator over the elements in this list (in proper * sequence). * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>. * @see #listIterator(int) */ public ListIterator<E> listIterator() { return new ListItr(0); }
而listIterator()的返回的ListIterator是由类ListItr向上转型得到的:
/** * An optimized version of AbstractList.ListItr */ private class ListItr extends Itr implements ListIterator<E> { ListItr(int index) { super(); cursor = index; } public boolean hasPrevious() { return cursor != 0; } public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } @SuppressWarnings("unchecked") 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]; } public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.set(lastRet, e); } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } 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(); } } }
我们先看ListItr类的带一个参数的构造函数:
ListItr(int index) { super(); cursor = index; }
这个构造函数先调用父类的构造函数建立一个迭代器,然后将index赋给游标cursor。通过这种方式,可以建立了一个指向集合中任意元素的迭代器。
注意,Itr中的游标cursor被初始化为0,指向集合中的第一个元素,调用iterator()方法并不能创建指向集合中任意元素的迭代器。
从源代码中我们可以看到,ListItr并没有重新实现Itr中已经实现的方法,直接从父类Itr中继承过来,比如hasNext()、next()、remove()和forEachRemaining()。
要理解next和previous相关的方法,先要认识到,在这里只说下一个元素、前一个元素,而不说当前元素,cursor指向的位置就是下一个元素的位置,这样下面的内容就容易理解了。
hasPrevious()
public boolean hasPrevious() { return cursor != 0; }
hasNext()判断的是cursor不等于size,是最后一个元素之后的位置;
而hasPrevious()方法判断的是cursor不等于0,是第一个元素的位置,它之前不再有元素。
nexIndex()和previousIndex()
public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; }
nextIndex()返回的是调用next()返回的元素的索引,previousIndex()返回的是此时调用previous()返回的索引。
previous()
@SuppressWarnings("unchecked") 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]; }
checkForComodification()是从类Itr继承来的,对结构是否被修改做判断;再判断i的范围,注意cursor可以取到size。
正常情况下,i的值肯定不会大于等于elementData.length这个值。
其实这里就是将cursor减1,然后返回cursor位置的元素,再将lastRet指向刚刚返回的元素的位置。
从这里我们可以看出,lastRet可能小于或等于cursor,但一定不会大于:
向前移动的时候,cursor大于lastRet;
反向移动的时候,cursor等于lastRet。
set(E e)
public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.set(lastRet, e); } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
替换的元素是lastRet位置的元素,而lastRest永远指向最近一次返回的元素。
add(E e)
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(); } }
add(E e)方法注意新元素插入的位置是在cursor之后就可以了,但是插入新元素之后,cursor加了1,也就是说add之后紧接着调用next()方法,返回的是add的元素。
插入新元素之后,lastRet赋值为-1,因为最近没有元素返回。
ArrayList源码解析(四)的更多相关文章
- ArrayList源码解析
ArrayList简介 ArrayList定义 1 public class ArrayList<E> extends AbstractList<E> implements L ...
- 顺序线性表 ---- ArrayList 源码解析及实现原理分析
原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7738888.html ------------------------------------ ...
- 面试必备:ArrayList源码解析(JDK8)
面试必备:ArrayList源码解析(JDK8) https://blog.csdn.net/zxt0601/article/details/77281231 概述很久没有写博客了,准确的说17年以来 ...
- ArrayList源码解析(二)
欢迎转载,转载烦请注明出处,谢谢. https://www.cnblogs.com/sx-wuyj/p/11177257.html 自己学习ArrayList源码的一些心得记录. 继续上一篇,Arra ...
- Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?
Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? 如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...
- Java中的容器(集合)之ArrayList源码解析
1.ArrayList源码解析 源码解析: 如下源码来自JDK8(如需查看ArrayList扩容源码解析请跳转至<Java中的容器(集合)>第十条):. package java.util ...
- Sentinel源码解析四(流控策略和流控效果)
引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...
- Collection集合重难点梳理,增强for注意事项和三种遍历的应用场景,栈和队列特点,数组和链表特点,ArrayList源码解析, LinkedList-源码解析
重难点梳理 使用到的新单词: 1.collection[kəˈlekʃn] 聚集 2.empty[ˈempti] 空的 3.clear[klɪə(r)] 清除 4.iterator 迭代器 学习目标: ...
- ArrayList源码解析--值得深读
ArrayList源码解析 基于jdk1.8 ArrayList的定义 类注释 允许put null值,会自动扩容: size isEmpty.get.set.add等方法时间复杂度是O(1): 是非 ...
- 【源码解析】- ArrayList源码解析,绝对详细
ArrayList源码解析 简介 ArrayList是Java集合框架中非常常用的一种数据结构.继承自AbstractList,实现了List接口.底层基于数组来实现动态容量大小的控制,允许null值 ...
随机推荐
- Twitter数据抓取的方法(一)
Scraping Tweets Directly from Twitters Search Page – Part 1 Published January 8, 2015 EDIT – Since I ...
- VUE进阶(路由等)
初级教程:http://www.cnblogs.com/dmcl/p/6137469.html VUE进阶 自定义指令 http://cn.vuejs.org/v2/guide/custom-dire ...
- 第九章 Criteria查询及注解
第九章 Criteria查询及注解9.1 使用Criteria查询数据 9.1.1 条件查询 Criteria查询步骤: 1)使用session接口的cr ...
- Unity C# 多态 委托 事件 匿名委托 Lambda表达式 观察者模式 .NET 框架中的委托和事件
一.多态 里氏替换原则: 任何能用基类的地方,可以用子类代替,反过来不行.子类能够在基类的基础上增加新的行为.面向对象设计的基本原则之一. 开放封闭原则: 对扩展开放,意味着有新的需求或变化时,可以对 ...
- hadoop环境搭建之关于NAT模式静态IP的设置 ---VMware12+CentOs7
很久没有更新了,主要是没有时间,今天挤出时间验证了一下,果然还是有些问题的,不过已经解决了,就发上来吧. PS:小豆腐看仔细了哦~ 关于hadoop环境搭建,从单机模式,到伪分布式,再到完全分布式,我 ...
- 利刃 MVVMLight 7:命令深入
上面一篇我们大致了解了命令的基本使用方法和基础原理,但是实际在运用命令的时候会复杂的多,并且有各种各样的情况. 一.命令带参数的情况: 如果视图控件所绑定的命令想要传输参数,需要配置 CommandP ...
- linux编译安装php7
1.首先下载php7 使用wget命令下载 wget http://cn2.php.net/distributions/php-7.0.12.tar.bz2 2.然后解压 tar -xvf php-7 ...
- AOJ/初等排序习题集
ALDS1_1_D-MaximumProfit. Codes: //#define LOCAL #include <cstdio> #include <algorithm> u ...
- 卷积神经网络CNN总结
从神经网络到卷积神经网络(CNN)我们知道神经网络的结构是这样的: 那卷积神经网络跟它是什么关系呢?其实卷积神经网络依旧是层级网络,只是层的功能和形式做了变化,可以说是传统神经网络的一个改进.比如下图 ...
- ORA-01034: ORACLE not available ORA-27101: shared memory realm does not exist
Oracle 设置默认数据库 如果我们的服务器上或者电脑上安装了多个数据库,当我们使用sqlplus时如果为指定数据库时登录到的是哪一个数据库呢?今天遇到了一个老问题: ORA-01034: ORAC ...