文章目录
- 一、概述
-
- [1.1 结构与角色](#1.1 结构与角色)
- [1.2 适用场景](#1.2 适用场景)
- 二、实现方式
-
- [2.1 自定义迭代器实现](#2.1 自定义迭代器实现)
- [2.2 多种遍历方式](#2.2 多种遍历方式)
- [2.3 迭代器模式 vs 直接遍历](#2.3 迭代器模式 vs 直接遍历)
- [三、JDK 源码中的迭代器](#三、JDK 源码中的迭代器)
-
- [3.1 Iterator 与 Iterable 接口](#3.1 Iterator 与 Iterable 接口)
- [3.2 ArrayList 的迭代器](#3.2 ArrayList 的迭代器)
- [3.3 LinkedList 的迭代器](#3.3 LinkedList 的迭代器)
- [3.4 HashMap 的迭代器](#3.4 HashMap 的迭代器)
- [3.5 for-each 与 Iterable 的关系](#3.5 for-each 与 Iterable 的关系)
- [3.6 三种迭代器对比](#3.6 三种迭代器对比)
- 四、总结
一、概述
在软件开发中,经常会遇到这样的场景:需要遍历一个集合对象中的元素,但不同的集合有着不同的内部结构------数组用下标访问、链表用指针遍历、树用递归遍历、哈希表用桶位遍历。如果让客户端直接操作集合的内部结构来遍历元素,就会产生强耦合------每更换一种集合类型,客户端的遍历代码就要重写:
下标遍历
指针遍历
递归遍历
桶位遍历
客户端代码
数组集合
链表集合
树形集合
哈希表集合
每更换集合类型就要重写遍历代码
迭代器模式(Iterator Pattern)正是为了解决这个问题而诞生的------它提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。迭代器模式将遍历行为从集合对象中分离出来,封装到一个独立的迭代器对象中,使得客户端可以用统一的接口遍历不同类型的集合。
生活中的迭代器模式例子:
- 超市收银台:无论购物车中放的是食品、日用品还是电器,收银员只需要一件一件地扫描商品,不需要关心商品的摆放方式
- MP3 播放器:无论歌曲存储在内存、SD 卡还是网络,"上一首""下一首"的操作方式始终一致
- 电视遥控器:无论电视接收的是有线电视、网络信号还是 HDMI 输入,"频道+"和"频道-"的操作方式一样
- 图书馆借阅:无论书籍按分类排列还是按编号排列,读者都可以一本一本地浏览
核心:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示
1.1 结构与角色
迭代器模式包含以下角色:
实现
实现
createIterator
持有引用
Client 客户端
Aggregate 抽象聚合
Iterator 抽象迭代器
ConcreteAggregate 具体聚合
ConcreteIterator 具体迭代器
- Iterator(抽象迭代器) :定义访问和遍历元素的接口,通常包含
hasNext()和next()方法 - ConcreteIterator(具体迭代器):实现抽象迭代器接口,跟踪当前遍历位置,对具体聚合对象进行遍历
- Aggregate(抽象聚合) :定义创建迭代器对象的接口,通常包含
createIterator()方法 - ConcreteAggregate(具体聚合):实现抽象聚合接口,返回一个与自身兼容的具体迭代器实例
- Client(客户端):通过迭代器接口遍历聚合对象中的元素,不直接操作聚合的内部结构
1.2 适用场景
- 需要访问一个聚合对象的内容,而又不希望暴露其内部表示
- 需要为多种聚合对象提供统一的遍历接口,客户端无需关心集合类型
- 需要支持多种遍历方式(如正序、倒序、按条件过滤等)遍历同一个聚合对象
- 希望将遍历行为从集合对象中分离,使集合类职责更加单一
二、实现方式
2.1 自定义迭代器实现
以"书架"为例,书架内部用数组存储书籍,客户端通过迭代器遍历所有书籍,无需知道书架内部是数组、链表还是其他数据结构。
实现
创建
实现
持有引用
客户端
BookShelf 具体聚合
Iterator 抽象迭代器
Aggregate 抽象聚合
BookShelfIterator 具体迭代器
(1)抽象迭代器------Iterator 接口
java
/**
* 抽象迭代器:定义遍历元素的接口
*
* @param <E> 元素类型
*/
public interface Iterator<E> {
/**
* 判断是否还有下一个元素
*
* @return 如果还有下一个元素返回 true,否则返回 false
*/
boolean hasNext();
/**
* 返回下一个元素
*
* @return 下一个元素
*/
E next();
}
(2)抽象聚合------Aggregate 接口
java
/**
* 抽象聚合:定义创建迭代器的接口
*
* @param <E> 元素类型
*/
public interface Aggregate<E> {
/**
* 创建迭代器
*
* @return 迭代器实例
*/
Iterator<E> createIterator();
}
(3)书籍实体
java
/**
* 书籍实体
*/
public class Book {
private final String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Book{name='" + name + "'}";
}
}
(4)具体聚合------书架
java
/**
* 具体聚合:书架
* 内部使用数组存储书籍,对外提供迭代器遍历
*/
public class BookShelf implements Aggregate<Book> {
private static final int MAX_SIZE = 100;
private final Book[] books;
private int size;
public BookShelf() {
this.books = new Book[MAX_SIZE];
this.size = 0;
}
/**
* 添加书籍
*
* @param book 书籍
*/
public void addBook(Book book) {
if (size >= MAX_SIZE) {
throw new IllegalStateException("书架已满,无法添加更多书籍");
}
books[size++] = book;
}
/**
* 获取指定位置的书籍
*
* @param index 索引
* @return 书籍
*/
public Book getBookAt(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("索引越界:" + index);
}
return books[index];
}
/**
* 获取书籍数量
*
* @return 书籍数量
*/
public int getSize() {
return size;
}
@Override
public Iterator<Book> createIterator() {
return new BookShelfIterator(this);
}
}
(5)具体迭代器------书架迭代器
java
/**
* 具体迭代器:书架迭代器
* 持有书架的引用,通过索引跟踪当前遍历位置
*/
public class BookShelfIterator implements Iterator<Book> {
private final BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
return index < bookShelf.getSize();
}
@Override
public Book next() {
if (!hasNext()) {
throw new java.util.NoSuchElementException("没有更多书籍了");
}
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
(6)客户端调用
java
public class BookShelfDemo {
public static void main(String[] args) {
// 创建书架并添加书籍
BookShelf bookShelf = new BookShelf();
bookShelf.addBook(new Book("《设计模式:可复用面向对象软件的基础》"));
bookShelf.addBook(new Book("《Effective Java》"));
bookShelf.addBook(new Book("《重构:改善既有代码的设计》"));
bookShelf.addBook(new Book("《代码整洁之道》"));
// 通过迭代器遍历------客户端不需要知道书架内部是数组
Iterator<Book> iterator = bookShelf.createIterator();
while (iterator.hasNext()) {
Book book = iterator.next();
System.out.println(book.getName());
}
// 《设计模式:可复用面向对象软件的基础》
// 《Effective Java》
// 《重构:改善既有代码的设计》
// 《代码整洁之道》
}
}
关键点 :客户端只依赖
Iterator接口和Aggregate接口,不依赖BookShelf的内部实现。即使将书架的存储结构从数组改为ArrayList,客户端代码也无需修改------只需修改BookShelf和BookShelfIterator即可。
2.2 多种遍历方式
迭代器模式的一个重要优势是:同一个聚合对象可以创建多种不同的迭代器,实现不同的遍历策略。以"学生列表"为例,支持正序遍历和倒序遍历两种迭代器:
(1)抽象迭代器------增加支持泛型
java
/**
* 抽象迭代器:支持泛型
*/
public interface Iterator<E> {
boolean hasNext();
E next();
}
(2)抽象聚合------支持泛型
java
/**
* 抽象聚合:支持泛型
*/
public interface Aggregate<E> {
Iterator<E> createIterator();
/**
* 创建倒序迭代器
*
* @return 倒序迭代器
*/
Iterator<E> createReverseIterator();
}
(3)学生实体
java
/**
* 学生实体
*/
public class Student {
private final String name;
private final int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
@Override
public String toString() {
return "Student{name='" + name + "', score=" + score + "}";
}
}
(4)具体聚合------学生列表
java
import java.util.ArrayList;
import java.util.List;
/**
* 具体聚合:学生列表
* 内部使用 List 存储,提供正序和倒序两种迭代器
*/
public class StudentList implements Aggregate<Student> {
private final List<Student> students = new ArrayList<>();
/**
* 添加学生
*
* @param student 学生
*/
public void addStudent(Student student) {
students.add(student);
}
/**
* 获取指定位置的学生
*
* @param index 索引
* @return 学生
*/
public Student getStudentAt(int index) {
return students.get(index);
}
/**
* 获取学生数量
*
* @return 学生数量
*/
public int getSize() {
return students.size();
}
@Override
public Iterator<Student> createIterator() {
return new StudentIterator(this);
}
@Override
public Iterator<Student> createReverseIterator() {
return new ReverseStudentIterator(this);
}
}
(5)正序迭代器
java
/**
* 具体迭代器:正序迭代器
*/
public class StudentIterator implements Iterator<Student> {
private final StudentList studentList;
private int index;
public StudentIterator(StudentList studentList) {
this.studentList = studentList;
this.index = 0;
}
@Override
public boolean hasNext() {
return index < studentList.getSize();
}
@Override
public Student next() {
if (!hasNext()) {
throw new java.util.NoSuchElementException("没有更多学生了");
}
return studentList.getStudentAt(index++);
}
}
(6)倒序迭代器
java
/**
* 具体迭代器:倒序迭代器
*/
public class ReverseStudentIterator implements Iterator<Student> {
private final StudentList studentList;
private int index;
public ReverseStudentIterator(StudentList studentList) {
this.studentList = studentList;
this.index = studentList.getSize() - 1;
}
@Override
public boolean hasNext() {
return index >= 0;
}
@Override
public Student next() {
if (!hasNext()) {
throw new java.util.NoSuchElementException("没有更多学生了");
}
return studentList.getStudentAt(index--);
}
}
(7)客户端调用
java
public class StudentListDemo {
public static void main(String[] args) {
StudentList studentList = new StudentList();
studentList.addStudent(new Student("张三", 85));
studentList.addStudent(new Student("李四", 92));
studentList.addStudent(new Student("王五", 78));
studentList.addStudent(new Student("赵六", 95));
// 正序遍历
System.out.println("=== 正序遍历 ===");
Iterator<Student> forward = studentList.createIterator();
while (forward.hasNext()) {
System.out.println(forward.next());
}
// Student{name='张三', score=85}
// Student{name='李四', score=92}
// Student{name='王五', score=78}
// Student{name='赵六', score=95}
// 倒序遍历
System.out.println("\n=== 倒序遍历 ===");
Iterator<Student> reverse = studentList.createReverseIterator();
while (reverse.hasNext()) {
System.out.println(reverse.next());
}
// Student{name='赵六', score=95}
// Student{name='王五', score=78}
// Student{name='李四', score=92}
// Student{name='张三', score=85}
}
}
关键点 :同一个
StudentList对象可以创建多种迭代器,每种迭代器实现不同的遍历策略。客户端只需选择合适的迭代器,不需要关心集合的内部结构和遍历算法。
2.3 迭代器模式 vs 直接遍历
| 对比维度 | 直接遍历集合内部 | 迭代器模式 |
|---|---|---|
| 耦合度 | 高(依赖集合内部结构) | 低(只依赖迭代器接口) |
| 遍历方式 | 每种集合不同 | 统一接口(hasNext + next) |
| 多种遍历 | 需修改集合类 | 新增迭代器类即可 |
| 集合替换 | 修改客户端代码 | 客户端无需修改 |
| 符合单一职责 | 否(集合既管存储又管遍历) | 是(存储和遍历分离) |
| 符合开闭原则 | 否 | 是 |
三、JDK 源码中的迭代器
迭代器模式在 JDK 中有着极为广泛的应用。java.util.Iterator 接口和 java.util.Iterable 接口是 JDK 对迭代器模式的标准化实现,几乎所有的 Java 集合类都实现了这两个接口。
3.1 Iterator 与 Iterable 接口
iterator()
hasNext()
next()
remove()
forEachRemaining()
extends
extends
extends
Iterable 接口
Iterator 接口
判断是否有下一个
获取下一个元素
移除当前元素
遍历剩余元素
Collection 接口
List 接口
Set 接口
Iterator 接口(JDK 源码简化版):
java
package java.util;
/**
* 迭代器接口
* JDK 对迭代器模式的标准实现
*/
public interface Iterator<E> {
/**
* 判断是否还有下一个元素
*/
boolean hasNext();
/**
* 返回下一个元素
*/
E next();
/**
* 从集合中移除迭代器返回的最后一个元素(可选操作)
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}
/**
* 对剩余的每个元素执行指定操作(JDK 8+)
*/
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext()) {
action.accept(next());
}
}
}
Iterable 接口(JDK 源码简化版):
java
package java.lang;
/**
* 可迭代接口
* 实现此接口的对象可以作为 for-each 循环的目标
*/
public interface Iterable<T> {
/**
* 返回迭代器
*/
Iterator<T> iterator();
/**
* 对每个元素执行指定操作(JDK 8+)
*/
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
/**
* 返回可分割迭代器(JDK 8+,用于并行流)
*/
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
关键点 :
Collection继承了Iterable,因此所有 Java 集合(List、Set、Queue等)都可以使用 for-each 循环遍历,本质上就是通过Iterator实现的。
3.2 ArrayList 的迭代器
ArrayList 是最常用的集合类之一,其内部迭代器 Itr 是迭代器模式在 JDK 中的经典实现。
java
// ArrayList 内部迭代器源码(简化版)
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
/**
* 内部迭代器:Itr
* 通过 cursor 游标跟踪遍历位置
*/
private class Itr implements Iterator<E> {
/** 下一个要访问的元素索引(游标) */
int cursor;
/** 最近一次访问的元素索引,-1 表示没有 */
int lastRet = -1;
/** 迭代器创建时集合的修改次数,用于 fail-fast 检测 */
int expectedModCount = modCount;
Itr() {}
@Override
public boolean hasNext() {
return cursor != size;
}
@Override
@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];
}
@Override
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();
}
}
/**
* fail-fast 检测
* 如果集合在迭代过程中被其他方式修改,立即抛出异常
*/
final void checkForComodification() {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
}
fail-fast 机制解析:
java
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
public class FailFastDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 错误示范:迭代过程中直接修改集合------触发 fail-fast
try {
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if ("B".equals(item)) {
list.remove(item); // 抛出 ConcurrentModificationException
}
}
} catch (ConcurrentModificationException e) {
System.out.println("触发 fail-fast:" + e.getClass().getSimpleName());
}
// 正确方式一:使用迭代器的 remove 方法
Iterator<String> it2 = list.iterator();
while (it2.hasNext()) {
String item = it2.next();
if ("B".equals(item)) {
it2.remove(); // 安全移除,会同步 expectedModCount
}
}
System.out.println("方式一结果:" + list);
// 方式一结果:[A, C]
// 正确方式二:使用 removeIf(JDK 8+)
list.add("D");
list.removeIf("D"::equals);
System.out.println("方式二结果:" + list);
// 方式二结果:[A, C]
}
}
fail-fast 机制 :迭代器在创建时记录集合的
modCount,每次调用next()时检查modCount是否改变。如果集合在迭代过程中被直接修改(非迭代器的remove方法),modCount会改变,迭代器检测到不一致后立即抛出ConcurrentModificationException,避免继续遍历产生不可预期的结果。
3.3 LinkedList 的迭代器
LinkedList 同时实现了 List 和 Deque 接口,其迭代器实现比 ArrayList 更复杂------支持双向遍历和列表修改操作。
java
// LinkedList 内部迭代器源码(简化版)
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
/**
* 内部迭代器:ListItr
* 支持双向遍历(hasPrevious / previous)和列表修改(add / set / remove)
*/
private class ListItr implements ListIterator<E> {
/** 最近返回的节点 */
private Node<E> lastReturned;
/** 下一个要访问的节点 */
private Node<E> next;
/** 下一个节点的索引 */
private int nextIndex;
/** 期望的修改次数 */
private int expectedModCount = modCount;
ListItr(int index) {
// 判断方向,选择从头或从尾开始查找
next = (index == size) ? null : node(index);
nextIndex = index;
}
@Override
public boolean hasNext() {
return nextIndex < size;
}
@Override
public E next() {
checkForComodification();
if (!hasNext()) {
throw new NoSuchElementException();
}
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
@Override
public boolean hasPrevious() {
return nextIndex > 0;
}
@Override
public E previous() {
checkForComodification();
if (!hasPrevious()) {
throw new NoSuchElementException();
}
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
@Override
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null) {
linkLast(e);
} else {
linkBefore(e, next);
}
nextIndex++;
expectedModCount = modCount;
}
final void checkForComodification() {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
}
使用方式:
java
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class LinkedListIteratorDemo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
// 正序遍历
System.out.println("=== 正序遍历 ===");
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
System.out.println(it.nextIndex() + " -> " + it.next());
}
// 0 -> A
// 1 -> B
// 2 -> C
// 3 -> D
// 倒序遍历
System.out.println("\n=== 倒序遍历 ===");
ListIterator<String> rit = list.listIterator(list.size());
while (rit.hasPrevious()) {
System.out.println(rit.previousIndex() + " -> " + rit.previous());
}
// 3 -> D
// 2 -> C
// 1 -> B
// 0 -> A
// 迭代过程中添加元素
System.out.println("\n=== 迭代中添加元素 ===");
ListIterator<String> addIt = list.listIterator();
while (addIt.hasNext()) {
String item = addIt.next();
if ("B".equals(item)) {
addIt.add("B+"); // 在 B 后面插入 B+
}
}
System.out.println(list);
// [A, B, B+, C, D]
}
}
3.4 HashMap 的迭代器
HashMap 的迭代器较为特殊,因为它需要遍历的是哈希桶数组中的链表/红黑树。HashMap 的迭代器分为三种:KeyIterator、ValueIterator、EntryIterator,它们共享相同的遍历逻辑,只是返回的内容不同。
java
// HashMap 内部迭代器源码(简化版)
public class HashMap<K, V> extends AbstractMap<K, V>
implements Map<K, V>, Cloneable, Serializable {
/**
* 基础迭代器:HashIterator
* 遍历桶数组 + 链表/红黑树
*/
abstract class HashIterator {
/** 下一个要返回的节点 */
Node<K, V> next;
/** 当前节点 */
Node<K, V> current;
/** 当前桶索引 */
int index;
/** 期望的修改次数 */
int expectedModCount;
HashIterator() {
expectedModCount = modCount;
Node<K, V>[] t = table;
current = next = null;
index = 0;
// 找到第一个非空桶
if (t != null && size > 0) {
do {
// 跳过空桶
} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {
return next != null;
}
public final Node<K, V> nextNode() {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (next == null) {
throw new NoSuchElementException();
}
Node<K, V> e = next;
current = e;
// 当前链表还有后续节点
if ((next = e.next) == null && table != null) {
// 当前链表遍历完了,找下一个非空桶
Node<K, V>[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
return e;
}
public final void remove() {
Node<K, V> p = current;
if (p == null) {
throw new IllegalStateException();
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
current = null;
removeNode(p.hash, p.key, null, false, false);
expectedModCount = modCount;
}
}
/** Key 迭代器 */
final class KeyIterator extends HashIterator implements Iterator<K> {
public K next() { return nextNode().key; }
}
/** Value 迭代器 */
final class ValueIterator extends HashIterator implements Iterator<V> {
public V next() { return nextNode().value; }
}
/** Entry 迭代器 */
final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K, V>> {
public Map.Entry<K, V> next() { return nextNode(); }
}
}
设计亮点 :
HashMap将三种迭代器的公共遍历逻辑抽取到HashIterator中,子类只需实现next()方法决定返回 key、value 还是 entry。这是模板方法模式与迭代器模式的结合运用。
3.5 for-each 与 Iterable 的关系
Java 5 引入的 for-each 循环语法,本质上就是迭代器模式的语法糖。任何实现了 Iterable 接口的类都可以使用 for-each 循环遍历:
java
import java.util.ArrayList;
import java.util.List;
public class ForEachDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// for-each 循环
for (String item : list) {
System.out.println(item);
}
// 等价于以下代码(编译器自动转换)
java.util.Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
}
}
}
自定义类支持 for-each:
java
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* 自定义范围类:支持 for-each 遍历
* 实现 Iterable 接口即可
*/
public class NumberRange implements Iterable<Integer> {
private final int start;
private final int end;
public NumberRange(int start, int end) {
if (start > end) {
throw new IllegalArgumentException("start 不能大于 end");
}
this.start = start;
this.end = end;
}
@Override
public Iterator<Integer> iterator() {
return new RangeIterator();
}
/**
* 范围迭代器
*/
private class RangeIterator implements Iterator<Integer> {
private int current = start;
@Override
public boolean hasNext() {
return current <= end;
}
@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return current++;
}
}
/**
* 客户端调用
*/
public static void main(String[] args) {
NumberRange range = new NumberRange(1, 5);
// 自定义类也可以使用 for-each 循环!
for (int num : range) {
System.out.print(num + " ");
}
// 1 2 3 4 5
}
}
要点 :只要实现
Iterable<T>接口并提供iterator()方法,任何自定义类都可以使用 for-each 循环。这是迭代器模式在日常开发中最常见的应用形式。
3.6 三种迭代器对比
| 对比维度 | ArrayList Itr | LinkedList ListItr | HashMap HashIterator |
|---|---|---|---|
| 遍历方向 | 单向(正序) | 双向 | 单向(桶 + 链表) |
| 内部数据结构 | 数组 | 双向链表 | 数组 + 链表/红黑树 |
| 位置跟踪 | cursor 游标 | Node 引用 | 桶索引 + Node 引用 |
| 支持 remove | 是 | 是 | 是 |
| 支持 add/set | 否 | 是 | 否 |
| fail-fast | 是 | 是 | 是 |
| 随机访问效率 | O(1) | O(n) | O(1) 平均 |
四、总结
迭代器模式的核心思想是提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
优点:
- 解耦集合与遍历:集合只负责存储,迭代器负责遍历,职责分离,符合单一职责原则
- 统一遍历接口:不同类型的集合使用相同的
hasNext()+next()接口遍历,客户端无需关心集合内部结构 - 支持多种遍历:同一个集合可以创建多种迭代器(正序、倒序、过滤等),符合开闭原则
- 集合可替换:客户端依赖迭代器接口而非具体集合,更换集合类型时客户端代码无需修改
- 支持 for-each:实现
Iterable接口即可使用 for-each 语法糖,简化客户端代码
缺点:
- 增加类的数量:每种集合都需要配套的迭代器类,增加了系统复杂度
- 遍历时不能直接修改集合:fail-fast 机制禁止在迭代过程中直接修改集合,否则抛出
ConcurrentModificationException - 迭代器与集合耦合:迭代器需要访问集合的内部数据,两者之间存在一定的耦合
- 无法回退遍历:普通的
Iterator只能单向前进,需要双向遍历要使用ListIterator
适用场景:
- 需要访问一个聚合对象的内容,而又不希望暴露其内部表示
- 需要为多种聚合对象提供统一的遍历接口
- 需要支持多种遍历方式遍历同一个聚合对象
- 希望自定义集合类支持 for-each 循环
迭代器模式与集合框架 :Java 集合框架(Collections Framework)是迭代器模式最成功的工业级应用。
Iterator接口是所有集合遍历的基础,Iterable接口让所有集合支持 for-each 语法,而ListIterator则在Iterator基础上扩展了双向遍历和修改操作。理解迭代器模式是深入掌握 Java 集合框架的关键。
参考博客:
迭代器模式 | 菜鸟教程:https://www.runoob.com/design-pattern/iterator-pattern.html