关于Iterator探究和思考

前两天,一哥们拿着同一个问题连续找了我两次。一开始以为没什么说的东西,后来越研究越觉得有意思,今天闲来无事,写出来跟大家分享。

问题是这样的:“迭代集合时,Iterator it=c.iterator() 返回的到底接口Iterator的哪个实现类?”。刚开始我随口就是“查查API不就知道了么”,后来证明查API还真就“不知道”。API显示Iterator只有三个实现类(BeanContextSupport.BCSIterator, EventReaderDelegate, Scanner),但是哪一个都不像是跟迭代有关的。后来查源码,发现Iterator设计和实现的精妙之处。

jdk源码 Iterator接口定义如下:

 public interface Iterator<E> {
 boolean hasNext();//判断容器内是否还有可供访问的元素
 E next();//返回迭代器刚越过的元素的引用,返回值是Object,需要强制转换成自己需要的类型
 void remove();//删除迭代器刚越过的元素
 }

我们都知道,其实在编程过程中经常使用的,也只有hasNext()和next()。一般我们都是这么进行迭代:

Iterator it=c.iterator();
while(it.hasNext()){
 Object o=it.next();
 //do something
}

不难发现,无论迭代的是List集合还是Set集合,也无论集合底层是采用数组实现的ArrayList、Vector、HashSet,或者是采用链表实现的LinkedList、又或者是采用二叉树实现的TreeSet,统统都是通过统一的方法hasNext()、next()来判断、获取下一个元素,但是具体的内部操作肯定是不一样的,那Iterator是怎么做到的呢?其实,并不是Iterator怎么做到,而是每一个集合类自己分别来进行实现的。下面我以ArrayList为例,跟大家一起分析一下jdk的精妙实现:

众所周知,ArrayList的内部实现采用数组,所以我们只需要记录相应位置的索引就可以了,其方法的实现也是比较简单的。它通过定义内部类内部类,来实现Iterator接口来实现的,如下:

 private class Itr implements Iterator<E> {
 int cursor = 0;//cursor从0开始,表示下一个元素的索引位置
 int lastRet = -1;//lastRet从-1开始,表示上一个元素的索引位置
 int expectedModCount = modCount;//记录对集合修改的次数,主要是用于实现ArrayList集合的快速失败机制
 /**
 *hasNext()只需判断当前位置是不是处在最后一个了即可.
 */
 public boolean hasNext() {
 return cursor != size();
 }
 /**
 * next()实现其实也是比较简单的,只要返回cursor索引位置处的元素即可,然后修改cursor、lastRet即可.
 */
 public E next() {
 checkForComodification();//主要用来判断集合的修改次数是否合法,即用来判断遍历过程中集合是否被修改过.
 try {
 E next = get(cursor);//从底层实现数组里取得当前元素
 lastRet = cursor++;//lastRet+1
 return next;//返回当前元素
 } catch (IndexOutOfBoundsException e) {
 checkForComodification();
 throw new NoSuchElementException();
 }
 }
 /**
 * 调用ArrayList本身的remove()方法删除lastRet位置元素,然后修改modCount即可.
 */
 public void remove() {
 if (lastRet == -1)
 throw new IllegalStateException();
 checkForComodification();
 try {
 AbstractList.this.remove(lastRet);//从底层实现数组里删除上一个元素
 if (lastRet < cursor)
 cursor--;//由于当前元素要填补到上一个元素的位置去,所以当前元素下标-1
 lastRet = -1;
 expectedModCount = modCount;//把集合的修改次数赋值给expectedModCount
 } catch (IndexOutOfBoundsException e) {
 throw new ConcurrentModificationException();
 }
 }
 
 final void checkForComodification() {
 if (modCount != expectedModCount)
 throw new ConcurrentModificationException();
 }
 }

这就是ArrayList的Iterator接口的实现。有人会问“那岂不是每一个集合类都要提供对Iterator的实现啊?”,对!Iterator只提供方法,你要想使用其进行迭代遍历,就必须提供对它的迭代实现。实际上,LinkedList、Vector、HashSet、TreeSet等集合的Iterator实现也都采用类似的设计思路。

通过以上探究,我们不难看出,Iterator给我们提供了一种通用的遍历各种集合的方法,它可以把访问逻辑从不同类型的集合类中抽象出来,做到了访问代码和集合本身的解耦,从而避免向客户端暴露集合的内部结构。从此,客户端可以不直接和集合类打交道,它只需要控制Iterator,向它发送“向前”、“向后”、“取当前元素”、“删元素”的命令,就可以间接遍历和操作整个集合,并且这些客户端代码还是可以复用的。其实,这就是java中非常著名的Iterator设计模式。

书到用时方恨少,事非经过不知难。点滴积累,你就会成为技术上的巨人!

欢迎工作一到五年的Java工程师朋友们加入Java高并发: 957734884,群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值