java中Iterator的思考

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

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

jdk源码 Iterator接口定义如下:

 

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

 

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

 

1
2
3
4
5
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接口来实现的,如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 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只提供方法,你要想使用其进行迭代遍历,就必须提供对它的迭代实现,记得以前java培训的老师这么讲过。实际上,LinkedList、Vector、HashSet、TreeSet等集合的Iterator实现也都采用类似的设计思路。

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

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

posted @ 2016-01-05 13:56  jinshiyill  阅读(267)  评论(0编辑  收藏  举报