迭代器模式
迭代器模式是在我们日常工作中常用到的一种。当我们使用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()
方法。