Iterator原理及实现
Iteratable
在Java中,容器可进行迭代操作,使用Iteratable接口来标识:
Iteratable中的接口如下:
public interface Iterable<T> {
//返回迭代器
Iterator<T> iterator();
//对每个元素执行action操作,提供了默认实现
//@since 1.8
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
//返回Spliterator
//@since 1.8
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}Collection接口继承了Iterable接口,这表明所有的Colection均支持迭代操作:
foreach示例代码
Iterator
Iterator,迭代器,用来遍历Collection中的元素。对于Collection的所有实现类而言,必须实现以下接口,即必须返回一个迭代器:
Iterator中的接口如下:
主要原理
创建指向某个
Collection的Iterator对象时,Iterator内部的游标指向的第一个元素。调用
hasNext()的时候,只是判断下一个元素的有无,并不移动游标。调用
next()的时候,返回游标指向的元素,之后向下移动游标,使其指向Collection中的下一个元素。调用
remove(),删除当前游标上次指向的元素
ArrayList中的实现
ArrayList中有两个Iterator的实现,一个Itr,另一个是增强的ListItr(提供了向前移动/遍历的功能,是ListIterator的实现),这里以Itr为例。
可知,在ArrayList内部的Iterator实现中,内部维护了一个游标cursor,该cursor初始化为0,每调用一次next()方法时,在返回cursor指向的元素之后,将curosr值加1;同时,使用变量lastRet来记录上次next()方法返回的元素的下标。在调用remove()方法后,由于实际数据量少了1个,所以将cursor指向lastRet,以便以便下次next()调用时可以返回正确的结果。
同时,也可以发现:在实现hasNext()/next()/remove()等方法时,均是直接操作或依赖ArrayList底层的数据结构。
在Collection其他子类中的实现中,也是如此。由于Collection中各实现类底层结构不尽相同,遍历时所采用的方式也就不一样,所以每个实现类一般都会各个维护自身的Iterator实现。
Map中的迭代
Map是键值对的集合。对于给定的一个map,我们可以:
使用map.keySet() 方法来获取所有的key的集合,类型为Set<K>(key值不会重复);
使用map.values() 方法来获取所有的value的集合,类型为Collection<V>;
使用map.entrySet()方法来获取所有键值对的集合,类型为Set<Map.Entry<K,V>>;
这几种Map的迭代方式,其实是降级到Collection再进行的。
下面是对Map进行遍历的一些常见用法:
但是,在JDK1.8及之后,HashMap中多了一种遍历的方法,该方法可以更直观地看到迭代方式的实现直接依赖底层数据结构:
而这种方式,使用时写法更为简明(借助lambda表达式):
参考
Last updated