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不是线程安全的,只能用在单线程环境下,多线 ...
随机推荐
- HDUOJ--------A simple stone game(尼姆博弈扩展)(2008北京现场赛A题)
A simple stone game ...
- Mahout推荐算法基础
转载自(http://www.geek521.com/?p=1423) Mahout推荐算法分为以下几大类 GenericUserBasedRecommender 算法: 1.基于用户的相似度 2.相 ...
- cisco nat
1.配置端口: int s0/0 ip nat outside ;s0/0为外部端口 int f0/0 ip nat inside ...
- Linux CC攻击脚本
CC(ChallengeCollapsar)主要是用来攻击页面的.大家都有这样的经历,就是在访问论坛时,如果这个论坛比较大,访问的人比较多,打开页面的速度会比较慢,访问的人越多,论坛的页面越多,数据库 ...
- Window["aaa"]这个在JS里是什么意思?
答案:定义一个全局的变量 aaa,这个的方式是数组,实际上是等于 window.aaa
- Spring mvc中使用request和response
@ResponseBody @RequestMapping(value="/ip", method=RequestMethod.GET) public String getIP(H ...
- eclipse ee 装oepe(Oracle Enterprise Pack for Eclipse)插件
eclipse j2ee 安装weblogic插件Oracle Enterprise Pack for Eclipse(oepe) eclipse j2ee已经集成了常见的几个服务器容器,比如tomc ...
- C语言中 scanf 和 printf 的小要点
1 scanf_s需指定%c 个数 h short l long关键字 * 不赋给任何值 W 指定读入宽度 转换字符 a 读浮点值(c99) A 读浮点值(c99) c 读单字符 d 读十进制数 i ...
- closeChrome
function closeChrome(){ var browserName=navigator.appName; if (browserName=="Netscape") { ...
- Java:String、StringBuffer和StringBuilder的区别
1 String String:字符串常量,字符串长度不可变.Java中String是immutable(不可变)的. String类的包含如下定义: /** The value is used fo ...