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值 ...
随机推荐
- error C2664: “UINT GetDriveTypeW(LPCWSTR)”: 无法将参数 1 从“char [5]”转换为“LPCWSTR”
解决方法:右击项目选择属性--->配置属性--->常规,将字符集改为“使用多字节字符符集”,应用确定即可. 来自为知笔记(Wiz)
- storage在IE8下的兼容性写法
storage 本地缓存,这是HTML5的一个非常好用的地方,具体好用在哪,网上可以找到很多,但是我觉得总结的都不是很完整,我建议大家有空的话可以看下JavaScript权威指南这本书,里面对于这个方 ...
- css3 felx布局
一.Flex布局是什么? Flex是Flexible Box的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性. 任何一个容器都可以指定为Flex布局. [css] .box ...
- VueJS 组件参数名命名方式和前台显示
camelCase(驼峰式) 和. kebab-case(短横线式) HTML 特性是不区分大小写的.所以,当使用的不是字符串模版,camelCased (驼峰式) 命名的 prop 需要转换为相对应 ...
- 【解决问题】解决python安装模块时UnicodeDecodeError
安装模块时,出现报错: UnicodeDecodeError: 'ascii' codec can't decode byte 0xcb in position 68: ordinal not in ...
- BackgroundWorker的DoWork方法中发生异常无法传递到RunWorkedCompleted方法
在使用C#的BackgroundWorker时需要在UI界面上显示DoWork中发生的异常,但怎么调试都无法跳转到界面上,异常也不会传递到RunWorkerCompleted方法中(e.Error为空 ...
- [进程管理] Linux中Load average的理解
Load average的定义 系统平均负载被定义为在特定时间间隔内运行队列中的平均进程树.如果一个进程满足以下条件则其就会位于运行队列中: - 它没有在等待I/O操作的结果 - 它没有主动进入等待状 ...
- 【DP系列学习一】简单题:kickstart2017 B.vote
https://code.google.com/codejam/contest/6304486/dashboard#s=p1 这是一道简单的dp,dp[i][j]代表A的voter为i,B的voter ...
- java线程(一)
java线程基础 什么是线程? 这里引用百度百科的一句话:"线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程ID,当 ...
- 关于WebGIS开源解决方案的探讨(转载)
1.背景 公司目前的多数项目采用的是ArcGIS产品+Oracle+WebLogic/Tomcat/APUSIC/WebShpere这样的架构.由于 公司从事的是政府项目,甲方单位普遍均采购有以上产品 ...