Iterator与Iterable(迭代器模式)
时间:2022-05-11 07:04
1、引言
在Java中,我们可以对List集合进行如下几种方式的遍历:
List list = new ArrayList<>();
// 法一:普通for循环
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}
//法二:迭代器遍历
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.print(it.next() + ",");
}
// for each遍历
for (Integer i : list) {
System.out.print(i + ",");
}
// 后面两种方式涉及到Java中的Iterator和Iterable对象
2、两者关系
看Iterable和Iterator所处包位置,Collection接口必须继承java.lang.Iterable接口。
Iterator为Java中的迭代器对象,是能够对List这样的集合进行迭代遍历的底层依赖。
Iterable接口里定义了返回Iterator的方法,相当于对Iterator的封装,同时实现了Iterable接口的类可以支持for each循环。
JDK中 Iterator 接口源码:
public interface Iterator {
boolean hasNext(); // 检查序列中是否还有元素
E next(); // 获取序列中下一个元素
default void remove() { // 将迭代器新返回的元素删除
throw new UnsupportedOperationException("remove");
}
// 1.8新增的Iterator接口中的默认方法,对集合中*剩余*的元素进行操作,直到元素完毕或者抛出异常;注意为“剩余”,即迭代中还剩余的部分
default void forEachRemaining(Consumer super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
集合类都实现了这个接口, Iterator 是迭代器最简单的实现,使用Iterator iter = list.iterator()
就实现了迭代器(见上面引言的使用)
Iterable接口的源码:
public interface Iterable {
Iterator iterator(); // 方法iterator()返回了一个Iterator对象(各具体类返回的类型也不同)
// 1.8新加的方法,对每个元素执行特有操作,可使用Lambda表达式
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
// 1.8新方法,可分割迭代器,用于并行遍历元素
default Spliterator spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
for each实现:为Java语法糖,也是依赖Iterator迭代器,编译器自动转化
for (Integer i : list);
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
i = (Integer)iterator.next();
}
3、为什么这样设计
这里是使用到了设计模式中的迭代器模式
-
为什么用Iterator实现遍历?(为什么使用迭代器模式)
- 用于遍历集合类的标准访问方法,它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构(其他索引遍历需要知道集合内部结构,且更换集合时需要重写遍历方法);
- 支持以不同的方式遍历一个聚合对象;
- 简化了聚合类;
- 在同一个聚合上可以有多个遍历;
- 便于增加迭代器类和新的聚合类。
-
为什么一定要去实现Iterable这个接口而不直接实现Iterator接口?
因为next()或者hasNext() 都依赖于迭代器当前的迭代位置,如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。
当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。
除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。 但这样,Collection也只能同时存在一个当前迭代位置。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。,使得多个迭代器是互不干扰的。 -
为什么不直接将hasNext(),next()方法放在Iterable接口中,其他类直接实现就可以了?
为了使集合类可以有多种遍历方式。