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值 ...
随机推荐
- C语言函数不定参数实现方式
函数如何实现不定参数: 由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,提出了指针参数来解决问题. (1)va_ ...
- Angular.js学习笔记(三)
一.过滤器 1.uppercase,lowercase 大小写转换{{ "lower cap string" | uppercase }} // 结果:LOWER CAP STRI ...
- HTML、CSS、JS 样式
把一个数组(一维或二维等)的内容转化为对应的字符串.相当于把print_r($array)显示出来的内容赋值给一个变量.$data= array('hello',',','world','!'); $ ...
- JS的内置对象以及JQuery中的部分内容
[js中的数组] 1 数组的概念:可以再内存中连续存储的多个有序元素的结构 元素的顺序:称为下标,通过下标查找对应元素. ...
- jquery分页插件的修改
前言 最近分页功能使用的比较多,所以从网上下载个jquery分页插件来使用, 之前用的都挺好的,直到昨天出现了逻辑问题,反复查看自己的代码,最后发现是点击页码后执行了多个点击事件.最后只有自己查看源码 ...
- 取消input默认样式
有时候input在页面中被聚焦后会出现默认的边框样式,可以通过以下方法取消去除 .input:focus{ outline: none;}
- poj3067树状数组求逆序数
Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Jap ...
- 在myeclipse下安装svn插件,出现了Could not generate DH keypair,这么一个错误。
解决方法: window-->preferences-->Team-->SVN-->SVN接口 选择:JavaHL 去https://sliksvn.com/download/ ...
- IOS的控制器
控制器简单来说,就是用来做界面跳转的,类似于 Android 的Intent 1.创建一个控制器 控制器的常见的创建方式 )通过storyboard创建 //直接创建 NJViewController ...
- OC中的单例
概念 单例模式的意图是类的对象称为系统中唯一的实例,提供一个访问点,供客户类共享资源 什么情况下使用单例 )类只能由一个实例,而且必须从一个为人熟知的访问点对其进行访问,比如工厂方法 )这个唯一的实例 ...