Java&设计模式——迭代器模式
=== 迭代器模式:在不暴露集合内部结构的前提下,顺序访问集合中各个元素。
在我们日常编写代码的时候,遍历集合的时候可能经常利用增强for循环的语法,例如下面这样:
public static void main(String[] args) {
List<Integer> list = Arrays.asList(11, 22, 33, 44, 555, 666, 1);
for (Integer i : list) {
System.out.println(i);
}
}
假设我们自定义一个类User
- User.java
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
User user = new User("zs", 18);
for (User u : user) {
}
/* Foreach 不适用于类型 'iterator.User' */
那我们可以思考下,什么类型才能放到foreach上,是不是大多数集合类都可以,所以可以看一下集合的源码。
public interface List<E> extends Collection<E> {
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* @return an iterator over the elements in this list in proper sequence
*/
Iterator<E> iterator();
}
这个方法是继承下来的,所以我们向上追溯源码
public interface Iterable<T> {
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
Iterator<T> iterator();
}
Iterable意思是可迭代的,然后这个接口定义了一个返回迭代器的方法,那我们尝试复刻一下,修改下User。
package iterator;
import java.util.Iterator;
/**
* @author: wangcai
* @date: 2026/01/26
*/
public class User implements Iterable<String>{
private String name;
private int age;
// 其余方法省略
@Override
public Iterator<String> iterator() {
return null;
}
}
这个时候我们再去测试一下,发现没有错误提示了。
public static void main(String[] args) {
User user = new User("zs", 18);
for (User u : user) {
System.out.println(u);
}
}
现在语法检查通过了,那我们直接运行一下看下。
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.Iterator.hasNext()" because "<local3>" is null
at iterator.Useage.main(Useage.java:18)
报了空指针异常,那很明显它调用了hasNext()得到了null值,那其实我们应该都知道
public static void main(String[] args) {
List<Integer> list = Arrays.asList(11, 22, 33, 44, 555, 666, 1);
for (Integer i : list) {
System.out.println(i);
}
// 上下两个代码等价的,具体可以看字节码,这里不做分析,底层代码就是相等的。
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
System.out.println(next);
}
}
等于说我们自己的代码也是这样的,对for增强进行遍历User的时候拿到迭代器以后执行了iterator.hasNext()报错的,那我们得完善下代码,将迭代器实现。
- User.java
/**
* @author: wangcai
* @date: 2026/01/26
*/
public class User implements Iterable<String>{
private String name;
private int age;
// 其余方法省略
@Override
public Iterator<String> iterator() {
return new MyIterator();
}
class MyIterator implements Iterator<String> {
// 我们就遍历类的属性即可。
int count = 2;
@Override
public boolean hasNext() {
return count > 0;
}
@Override
public String next() {
if (count == 1) {
count--;
return String.valueOf(User.this.age);
}
if (count == 2) {
count--;
return User.this.name;
}
throw new NoSuchElementException();
}
}
}
- Usage.java
public class Useage {
public static void main(String[] args) {
User user = new User("zs", 18);
for (String u : user) {
System.out.println(u);
}
}
}
/*
zs
18
*/
大功告成了,那我们可以得出,迭代器语法并不是固定提供给特定结构使用的,而是一种通用的设计模式,只要我们按照语法规则和设计思想开发即可,对我们想要迭代的对象实现可迭代接口,并且编写对应的迭代器实现迭代逻辑即可,等于对外屏蔽了内部的数据结构,使用方只需要调用迭代器进行外部迭代拿到结果,而不关心内部的存储结构,这样哪怕我们的存储结构由List换为了Set外部也是无感知的,解耦之后使代码更加健壮,我们可以利用多态直接更换迭代器实现达成不同的遍历逻辑,源码改变了调用方代码也无需改变。
现在我们可以学习一下官方的实践源码,学习的最好方法就是学习下官方源码,我们学习下ArrayList。

看下官方的迭代器的实现
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;
// prevent creating a synthetic constructor
Itr() {}
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)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int size = ArrayList.this.size;
int i = cursor;
if (i < size) {
final Object[] es = elementData;
if (i >= es.length)
throw new ConcurrentModificationException();
for (; i < size && modCount == expectedModCount; i++)
action.accept(elementAt(es, i));
// update once at end to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
源码中hasNext()用cursor来记录遍历的元素数量,只要不等于List的长度就代表还没有遍历完成。
然后我们详细看下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];
}
我们能看出来官方的实现比我们多了checkForComodification这个方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这个modCount初始化为0,然后进行add的时候会+1

意味着我们获取迭代器的时候,会将expectedModCount这个预期的值更新为modCount,而modCount的值会随着你使用add操作的时候++。
为什么这么设计呢?你可以将modCount视为List的版本号,当我们迭代时进行了并发修改就导致了两边版本号不一致,所以抛出了异常。
所以说调用next操作的时候,两个值如果不相等就会抛异常ConcurrentModificationException
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
// 1能打印 2后面都不行 因为第二次next调用checkForComodification检查就会出错了
System.out.println(iterator.next());
list.add(131);
}
}

更多推荐





所有评论(0)