迭代器模式
迭代器模式是在我们日常工作中常用到的一种。当我们使用Java遍历集合的时候,使用的就是迭代器模式。为了理解迭代器模式,我们就那Java中集合遍历的代码来解释分析一下。
// 定义Student类,省略了构造方法和getter/setter
public class Student {
private String name;
private String address;
private String gender;
}
// 定义client
public class Client {
public static void main(String[] args) {
List<Student> studentList = studentGenerator();
Iterator<Student> iterator = studentList.iterator();
while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student);
}
}
private static List<Student> studentGenerator() {
Student s1 = new Student("张三", "北京", "男");
Student s2 = new Student("李四", "天津", "男");
Student s3 = new Student("王五", "河北", "男");
Student s4 = new Student("赵柳", "北京", "女");
List<Student> studentList = new ArrayList<>(4);
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
return studentList;
}
}
// 输出内容
Student{name='张三', address='北京', gender='男'}
Student{name='李四', address='天津', gender='男'}
Student{name='王五', address='河北', gender='男'}
Student{name='赵柳', address='北京', gender='女'} 通过上面我们看到通过Iterator(迭代器)遍历学生集合,其中最关键的2个方法就是:
hasNext()判断迭代的集合中是否还存在元素next()获取当前元素
为了一探究竟,我们先看看 Iterator 的源码:
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
} Iterator 是一个接口,提供了4个方法,其中2个有默认实现,另外2个没有默认实现的就是 hasNext() 和 Next() 。由于上文中我们使用的是 ArrayList ,接下来我们看看 ArrayList 是如何实现这2个方法的。通过阅读源码我们可以发现, ArrayList 内部定义了一个 Itr 实现了 Iterator 接口。源码如下(对 hasNext() 和 next() 方法加了注释):
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;
Itr() {}
// 判断迭代的元素下标是否和集合元素个数相等,如果相等说明没有多余的元素了。
public boolean hasNext() {
return cursor != size;
}
// 最重要的就是最后一行代码,直接返回elementData[i],从数组选取第i个元素返回。其他的就是一些校验可以暂时忽略
@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
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
} 通过上面,我们可以看出迭代器需要有以下几种元素:
- 抽象集合:即里面的
List<T>。(如果只有一个元素为什么还需要迭代遍历呢?是吧。) - 具体的集合:即里面的
ArrayList<Student>。需要明确迭代哪个集合。 - 抽象的迭代器:即
Iterator<T> - 具体的迭代器:即
ArrayList里面的Itr
在日常工作中,如果想需要去需要自己实现迭代器的话,其实就实现Java中的 Iterator 就可以了。当然你也可以自己搞一下自己的抽象迭代器接口,但是必需要有最关键 hasNext() 和 next() 方法。
