Java 集合源码解析(2):ListIterator
点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~
今天心情和股票一样红,还是学学 ListIterator 吧!
ListIterator
根据官方文档介绍, ListIterator 有以下功能:
- 允许我们向前、向后两个方向遍历 List;
- 在遍历时修改 List 的元素;
- 遍历时获取迭代器当前游标所在位置。
注意,迭代器 没有当前所在元素一说,它只有一个游标( cursor )的概念,这个游标总是在元素之间,比如这样:
初始时它在第 0 个元素之前,调用 next() 游标后移一位:
调用 previous() 游标就会回到之前位置。当向后遍历完元素,游标就会在元素 N 的后面:
也就是说长度为 N 的集合会有 N+1 个游标的位置。
ListIterator 继承自 Iterator 接口(关于 Iterator 的介绍 请点这里),在 Iterator 的基础上增加了 6 个方法:
介绍一下新来的几个方法:
- void hasPrevious()
- 判断游标前面是否有元素;
- Object previous()
- 返回游标前面的元素,同时游标前移一位。游标前没有元素就报 java.util.NoSuchElementException 的错,所以使用前最好判断一下;
- int nextIndex()
- 返回游标后边元素的索引位置,初始为 0 ;遍历 N 个元素结束时为 N;
- int previousIndex()
- 返回游标前面元素的位置,初始时为 -1,同时报 java.util.NoSuchElementException 错;
- void add(E)
- 在游标 前面 插入一个元素
- 注意,是前面
- void set(E)
- 更新迭代器最后一次操作的元素为 E,也就是更新最后一次调用 next() 或者 previous() 返回的元素。
- 注意,当没有迭代,也就是没有调用 next() 或者 previous() 直接调用 set 时会报 java.lang.IllegalStateException 错;
- void remove()
- 删除迭代器最后一次操作的元素,注意事项和 set 一样。
ListIterator 有两种获取方式
- List.listIterator()
- List.listIterator(int location)
区别在于第二种可以指定 游标的所在位置。
ListIterator 的具体实现?
AbstractList 作为 List 的直接子类,里面实现了 listIterator() 方法,并且有两个内部迭代器实现类:SimpleListIterator,FullListIterator:
listIterator() 返回的是 FullListIterator():
FullListIterator 继承了 SimpleListIterator, SimpleListIterator 实现了 Iterator 接口:
private class SimpleListIterator implements Iterator<E> {
//游标的位置,初始为 -1
int pos = -1;
//用来判断是否 fail-fast 的变量
int expectedModCount;
//记录上次迭代的位置
int lastPosition = -1;
SimpleListIterator() {
expectedModCount = modCount;
}
//当游标没有跑到最后一个元素后面时 hasNext 返回 true
public boolean hasNext() {
return pos + 1 < size();
}
//获取下一个元素
public E next() {
if (expectedModCount == modCount) {
try {
//获取游标后面的元素,具体子类有具体实现
E result = get(pos + 1);
//更新
lastPosition = ++pos;
return result;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
//当迭代时修改元素,就会报这个错,上篇文章介绍过解决办法~
throw new ConcurrentModificationException();
}
//删除上次迭代操作的元素
public void remove() {
//还没进行迭代操作就会报这个错
if (this.lastPosition == -1) {
throw new IllegalStateException();
}
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
try {
//调用子类实现的删除操作
AbstractList.this.remove(lastPosition);
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
expectedModCount = modCount;
if (pos == lastPosition) {
pos--;
}
//每次删除后都会还原为 -1,也就是说我们迭代一次后只能 remove 一次,再 remove 就会报错
lastPosition = -1;
}
}
了解了 SimpleListIterator 后我们看下 FullListIterator 的具体实现:
private final class FullListIterator extends SimpleListIterator implements ListIterator<E> {
//根据 start 指定游标位置
FullListIterator(int start) {
if (start >= 0 && start <= size()) {
pos = start - 1;
} else {
throw new IndexOutOfBoundsException();
}
}
//在游标前面添加元素
public void add(E object) {
if (expectedModCount == modCount) {
try {
//调用子类的添加操作,ArrayList, LinkedList,Vector 的添加操作实现有所不同
AbstractList.this.add(pos + 1, object);
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
//游标后移一位
pos++;
//!注意! 添加后 上次迭代位置又变回 -1 了,说明 add 后调用 remove, set 会有问题!
lastPosition = -1;
if (modCount != expectedModCount) {
expectedModCount = modCount;
}
} else {
throw new ConcurrentModificationException();
}
}
//当游标不在初始位置(-1)时返回true
public boolean hasPrevious() {
return pos >= 0;
}
//游标后面的元素索引,就是游标 +1
public int nextIndex() {
return pos + 1;
}
//游标前面一个元素
public E previous() {
if (expectedModCount == modCount) {
try {
E result = get(pos);
lastPosition = pos;
pos--;
return result;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
throw new ConcurrentModificationException();
}
//游标前面元素的索引,就是游标的位置,有点晕的看开头那几张图
public int previousIndex() {
return pos;
}
//更新之前迭代的元素为 object
public void set(E object) {
if (expectedModCount == modCount) {
try {
//调用子类的set
AbstractList.this.set(lastPosition, object);
} catch (IndexOutOfBoundsException e) {
throw new IllegalStateException();
}
} else {
throw new ConcurrentModificationException();
}
}
}
可以看到 SimpleListIterator 的主要操作最后都交给子类来实现,List 的子类 ArrayList, LinkedList, Vector 由于底层实现原理不同(数组,双向链表),具体操作类实现有所不同。
等接下来分析到具体子类再看相关实现吧。
Java 集合源码解析(2):ListIterator的更多相关文章
- java集合 源码解析 学习手册
学习路线: http://www.cnblogs.com/skywang12345/ 总结 1 总体框架 2 Collection架构 3 ArrayList详细介绍(源码解析)和使用示例 4 fai ...
- Java 集合源码解析(1):Iterator
Java, Android 开发也有段时间了,当初为了早点学 Android,Java 匆匆了解个大概就结束了,基础不够扎实. 虽然集合框架经常用,但是一直没有仔细看看原理,仅止于会用,不知道为什么要 ...
- java集合源码分析几篇文章
java集合源码解析https://blog.csdn.net/ns_code/article/category/2362915
- Java集合源码分析(三)LinkedList
LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ...
- 【转】Java HashMap 源码解析(好文章)
.fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...
- Java集合类源码解析:Vector
[学习笔记]转载 Java集合类源码解析:Vector 引言 之前的文章我们学习了一个集合类 ArrayList,今天讲它的一个兄弟 Vector.为什么说是它兄弟呢?因为从容器的构造来说,Vec ...
- java集合源码分析(三):ArrayList
概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...
- Java集合源码分析(四)Vector<E>
Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是 ...
- Java集合源码分析(二)ArrayList
ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...
随机推荐
- hdu----(1847)Good Luck in CET-4 Everybody!(简单巴什博奕)
Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- poj1122 FDNY to the Rescue!(dij+反向建图+输出路径)
题目链接:poj1122 FDNY to the Rescue! 题意:给出矩阵,矩阵中每个元素tij表示从第i个交叉路口到第j个交叉路口所需时间,若tij为-1则表示两交叉路口之间没有直接路径,再给 ...
- jQuery_pager.js分页
在做前端项目中,总是需要自己手写类似于这样的分页效果: 这就需要使用jQuery.pager.js文件,其使用方法为:在html中引入三个文件,分别为: <link rel="styl ...
- UVa 11361 - Investigating Div-Sum Property
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&p ...
- LCA-倍增法(在线)
原文:http://www.tuicool.com/articles/N7jQV32 1. DFS预处理出所有节点的深度和父节点 inline void dfs(int u) { int i; for ...
- JAVA中StringBuffer类常用方法详解
String是不变类,用String修改字符串会新建一个String对象,如果频繁的修改,将会产生很多的String对象,开销很大.因此java提供了一个StringBuffer类,这个类在修改字符串 ...
- Oracle ODP.NET 篇
1.C# 使用 System.Data.OracleClient 连接 Oracle 需要安装 instantclient , 并配置相应环境变量.重启,方可使用. 2. 使用 System.Data ...
- SQLite实现Top功能
SQlite本身没有top功能,无法向TSQL一样下Select top 100 * from tb_table,但SQLite提供了一个Limit关键字用来取得相应行数的资料 具体语法实例:Sele ...
- C++中各种容器的类型与特点
1.vector 连续存储结构,每个元素在内存上是连续的: 支持高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下: 2.deque 连续存储结构,即其每个元素在内存上也是连续的 ...
- git初识
下载网上的开发员的项目 git clone https://github.com/xxxxxx/test.git 下载好,进入刚下载的好的test项目目录,可以修改对应的代码,然后 git statu ...