https://github.com/yzmaodeng/java-keypointknowledge/commit/92cffe84f620f4a82cb001811a90fe15ef165e86
迭代器模式
定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。
类型:行为类模式
类图:
使用迭代器模式,我们可以自己定义接口,但是在JDK中已经有定义好的迭代器接口,可以满足大部分的使用场景:
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());
}
}
一般情况,我们只需要实现hasNext和next方法就可以了。
接下来我们就下一个简单的例子:
定义自己的集合MyCollection,通过迭代器iterator,按照正序返回集合中的对象。
首先,定义我们的MyCollection类:
/**
* Created by li.zhipeng on 2017/9/21.
*
* 自定义集合类
*/
class MyCollection<T>{
private List<T> list = new ArrayList<>();
public void add(T item){
list.add(item);
}
public void remove(T item){
list.remove(item);
}
public Iterator<T> iterator(){
return new MyIterator(list);
}
/**
* 自定义迭代器
* */
private class MyIterator implements Iterator<T>{
private List<T> dataSource;
/**
* 当前位置的指针
* */
private int curPos;
public MyIterator(List<T> data){
this.dataSource = data;
}
@Override
public boolean hasNext() {
return curPos < dataSource.size();
}
@Override
public T next() {
T item = dataSource.get(curPos);
curPos ++;
return item;
}
}
}
为了简单方便,我在MyCollection内部使用了ArrayList,然后封装了两个方法,添加和删除ArrayList中的元素,iterator()方法返回我们自定义的迭代器,这里还使用了泛型,规定了添加元素的类型,MyCollection的泛型肯定是要和迭代器是一样的,这里就不多说了。
请注意这里的迭代器最好不要公用,尽量返回一个新的迭代器对象,因为如果多线程同时遍历迭代器,这里可能会影响到迭代器的指针,所以我每次都是创建一个新的迭代器。如果你更加关心多线程的并发操作,这里最好每次创建Iterator的时候,应该加入同步锁,传入List的副本,提高多线程并发的安全性。
然后在MyCollection中定义了内部迭代器MyIterator,有一个私有属性curPos,记录遍历的位置指针。
使用private修饰迭代器的原因
之前说过Iterator需要隐藏集合遍历时获取元素的规则,并且这个Iterator也只为我们当前的MyCollection服务,所以公开这个类是一个不明智的选择,我这里选择隐藏这个类,只能通过MyCollection的iterator方法来获取。
大部分的迭代器模式开发中,往往都是隐藏自定义迭代器。
定义完毕,测试一下我们的MyCollection:
public void test(){
MyCollection<Student> collection = new MyCollection<>();
collection.add(new Student("1111"));
collection.add(new Student("2222"));
Iterator<Student> iterator = collection.iterator();
while (iterator.hasNext()){
Student student = iterator.next();
System.out.println(student.getName());
}
}
当我还沉迷于自己的才华无法自拔的时候,产品君说了:我们现在要改成倒序遍历了,你抓紧时间弄一下,明天我们要上线。
如果我之前使用的是for循环,那么我就需要根据功能逻辑,找到遍历的代码,然后一处一处的修改,如果有个100使用的地方,我觉得今晚是没法睡了。
但是机智如我,我早就看穿了产品君骚动的内心,早早的使用了迭代器模式,我只需要对MyIterator做一点小调整:
/**
* 自定义迭代器
* */
private class MyIterator implements Iterator<T>{
private List<T> dataSource;
/**
* 当前位置的指针
* */
private int curPos;
public MyIterator(List<T> data){
this.dataSource = data;
// 把遍历开始的指针,放到最后一个
curPos = data.size() - 1;
}
@Override
public boolean hasNext() {
// 修改是否遍历的结束条件,这里需要大于0
// return curPos < dataSource.size();
return curPos >= 0;
}
@Override
public T next() {
T item = dataSource.get(curPos);
// 这里要把位置--
// curPos ++;
curPos--;
return item;
}
}