迭代器源码分析(基于 ArrayList
)
iterator源码分析
1. 迭代器的基本定义
在 ArrayList
中,调用 list.iterator()
时,实际上是通过 ArrayList
内部的 Itr
类实现的迭代器。其源码如下:
public Iterator<E> iterator() {
return new Itr(); // 返回迭代器的实例
}
此时,会创建 Itr
类的一个实例,这个类实现了 Iterator
接口,并负责对 ArrayList
进行元素遍历。
默认初始化:cursor = 0,所以游标指向 0 索引位置。
2. 迭代器的核心方法
2.1 hasNext()
方法
hasNext()
方法用于检查迭代器是否还有未被遍历的元素,它的实现逻辑非常简单,只需判断当前指针 cursor
是否到达数组末尾。
public boolean hasNext() {
return cursor != size; // 如果游标没有超出元素数量,表示还有元素可以遍历
}
2.2 next()
方法
next()
是迭代器的核心方法,它负责返回当前指针位置的元素,同时将指针向前移动一个位置:
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
用来跟踪当前的遍历位置,每调用一次 next()
,游标就会右移,直到所有元素被遍历完成。
2.3 remove()
方法
remove()
用于删除最后一次调用 next()
时返回的元素:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); // 调用 `ArrayList` 的删除方法
cursor = lastRet; // 更新游标位置
lastRet = -1; // 重置 lastRet
expectedModCount = modCount; // 更新修改计数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
该方法通过内部的 ArrayList
的 remove()
方法来完成元素删除,删除后游标 cursor
会重置为删除元素的位置。
3. 内部状态及安全机制
3.1 cursor
和 lastRet
cursor
:表示迭代器当前指向的索引位置。lastRet
:表示最后一次通过next()
方法返回的元素索引,初始值为-1
。
3.2 modCount
和 expectedModCount
-
modCount
:记录集合结构修改的次数。 -
expectedModCount
:迭代器期望的修改次数。每次创建迭代器时,它会将modCount
赋值给expectedModCount
。如果在遍历过程中,集合发生了结构修改(如增加、删除元素),这两个值不再相等,会抛出ConcurrentModificationException
异常,确保遍历的安全性。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
3.3 并发修改异常
在遍历集合时,如果直接使用集合的 add()
或 remove()
等方法,会导致 modCount
发生变化,从而引发 ConcurrentModificationException
异常。
总结:
- 迭代器封装了
ArrayList
的遍历操作,确保集合遍历的安全性和统一性。 - 使用迭代器时,避免直接通过集合方法修改集合,否则可能引发并发修改异常。
ListIterator
源码分析
ListIterator
是 Iterator
的子接口,专为 List
设计,提供了在双向遍历列表和修改元素方面的更多功能。与 Iterator
不同,ListIterator
可以在遍历过程中向前或向后移动,并允许元素修改、添加等操作。
1. ListIterator
的基本功能
ListIterator
是一个功能增强版的迭代器,提供了如下功能:
- 双向遍历:可以通过
hasPrevious()
和previous()
方法向前遍历列表。 - 元素修改:可以通过
set()
方法修改列表中的元素。 - 添加元素:可以通过
add()
方法在列表中添加元素。
2. ArrayList
中的 ListIterator
实现
在 ArrayList
中,通过 list.listIterator()
方法可以获取 ListIterator
实例。内部实现与普通迭代器类似,不过提供了更多的方法,支持双向遍历和元素修改。
2.1 创建 ListIterator
我们来看 ArrayList
中 ListIterator
的创建方法:
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index); // 返回 ListItr 实例
}
可以看到,ListItr
类是 ListIterator
的实现类。该方法会根据传入的索引创建一个 ListItr
实例,并将当前遍历位置设置为指定的索引。
2.2 ListItr
类的定义
ListItr
继承自 Itr
,也就是普通的迭代器类,并在此基础上添加了双向遍历和修改列表的功能:
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super(); // 调用父类的构造方法
cursor = index; // 设置游标为指定位置
}
public boolean hasPrevious() {
return cursor != 0; // 游标是否位于列表开头
}
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]; // 返回前一个元素,并更新 lastRet
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e); // 调用 ArrayList 的 set 方法
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e); // 调用 ArrayList 的 add 方法
cursor = i + 1; // 游标前移
lastRet = -1;
expectedModCount = modCount; // 更新 expectedModCount
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
3. ListIterator
的核心方法
3.1 hasPrevious()
和 previous()
这些方法用于向前遍历列表。hasPrevious()
检查是否存在前一个元素,previous()
则返回前一个元素,并将游标向前移动。
public boolean hasPrevious() {
return cursor != 0; // 如果游标不在开头,就有前一个元素
}
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]; // 返回前一个元素
}
3.2 nextIndex()
和 previousIndex()
这两个方法返回当前游标所指向的下一个和前一个元素的索引,分别用于向前和向后遍历时的索引位置:
public int nextIndex() {
return cursor; // 当前游标即为下一个元素的索引
}
public int previousIndex() {
return cursor - 1; // 当前游标减一即为前一个元素的索引
}
3.3 set()
和 add()
set()
方法允许在列表中修改最近一次通过 next()
或 previous()
返回的元素:
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
ArrayList.this.set(lastRet, e); // 通过 ArrayList 的 set 方法修改元素
}
add()
方法允许在列表中添加新元素,添加后的元素位于当前游标位置:
public void add(E e) {
checkForComodification();
int i = cursor;
ArrayList.this.add(i, e); // 在游标位置添加元素
cursor = i + 1; // 游标前移
lastRet = -1; // 重置 lastRet
expectedModCount = modCount; // 更新 expectedModCount
}
4. ListIterator
的并发安全性
与 Iterator
类似,ListIterator
同样使用 modCount
和 expectedModCount
来检测集合的结构是否发生了并发修改。如果在迭代过程中检测到 modCount
与 expectedModCount
不一致,就会抛出 ConcurrentModificationException
异常。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这个机制确保在遍历过程中,如果集合被其他线程或方法修改,能够立刻发现并抛出异常。
5. ListIterator
的使用示例
下面是一个简单的使用 ListIterator
遍历和修改 ArrayList
的示例:
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
String str = it.next();
if ("B".equals(str)) {
it.set("BB"); // 修改元素
it.add("BBB"); // 添加新元素
}
}
System.out.println(list);
输出结果:
[A, BB, BBB, C]
可以看到,使用 ListIterator
能够灵活地在遍历过程中修改或添加元素。
ator it = list.listIterator();
while (it.hasNext()) {
String str = it.next();
if (“B”.equals(str)) {
it.set(“BB”); // 修改元素
it.add(“BBB”); // 添加新元素
}
}
System.out.println(list);
输出结果:
```java
[A, BB, BBB, C]
可以看到,使用 ListIterator
能够灵活地在遍历过程中修改或添加元素。