前两天,一哥们拿着同一个问题连续找了我两次。一开始觉得没什么说的东西,后来越研究越以为有意思,今天闲来无事,写出来跟你们分享。java
问题是这样的:“迭代集合时,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的精妙实现:ide
众所周知,ArrayList的内部实现采用数组,因此咱们只须要记录相应位置的索引就能够了,其方法的实现也是比较简单的。它经过定义内部类内部类,来实现Iterator接口来实现的,以下:this
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设计模式。索引
书到用时方恨少,事非通过不知难。点滴积累,你就会成为技术上的巨人!接口