Iterator设计模式

以前讲了两种List,一种基于数组实现的ArrayList,一种基于链表实现的LinkedList,这两种list是咱们工做中最经常使用到的List容器。固然数组和链表也是两种常见的基本数据结构,其余基本数据结构还有堆栈、队列、树等,对java容器的学习,也能够看作是对数据结构的学习和使用。java

 
     在ArrayList和LinkedList的分析中,都没有对容器遍历进行分析,前面说过迭代器相对独立和复杂,并且它又是一种很是经典的 设计模式(说经典不过使用场景也就这一个…),这里开始对Iterator进行分析。
     细心的童鞋会看到这边blog的标题叫作”设计模式”而不是前面的”源码解析”,是的这里会从设计模式层面出发,更偏重于怎么更好的进行与改善代码的设计。
 
1.统一接口
 
     先想一个问题,如今有ArrayList和LinkedList两种容器,咱们怎么实现容器的可替换性呢?什么叫作可替换性,就是我底层的容器实现能够随意改变,而对用户的使用不产生任何影响,说到这里应该都明白了就是要统一接口,用户只须要面向接口编程对不对(这里以及下面说的用户都是指开发者)。来看看ArrayList和LinkedList是否是这样作的(伪装你还不知道的状况下,一块儿看吧^_^)?
 
  ArrayList的定义:
1
2
public class ArrayList<E> extends AbstractList<E>
         implements List<E>, RandomAccess, Cloneable, java.io.Serializable

 

LinkedList的定义:编程

1
2
3
public class LinkedList<E>
           extends AbstractSequentialList<E>
           implements List<E>, Deque<E>, Cloneable, java.io.Serializable

 

   从定义上能够看到ArrayList和LinkedList都实现了List接口,ok看下List接口的定义:设计模式

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
public interface List<E> extends Collection<E> {
           int size();
           boolean isEmpty();
           boolean contains(Object o);
           Iterator<E> iterator();
           Object[] toArray();
           <T> T[] toArray(T[] a);
           boolean add(E e);
           boolean remove(Object o);
          boolean containsAll(Collection<?> c);
          boolean addAll(Collection<? extends E> c);
          boolean addAll( int index, Collection<? extends E> c);
          boolean removeAll(Collection<?> c);
          boolean retainAll(Collection<?> c);
          void clear();
          boolean equals(Object o);
          int hashCode();
          E get( int index);
          E set( int index, E element);
          void add( int index, E element);
          E remove( int index);
          int indexOf(Object o);
          int lastIndexOf(Object o);
          ListIterator<E> listIterator();
          ListIterator<E> listIterator( int index);
          List<E> subList( int fromIndex, int toIndex);
}

能够看到List中对容器的各类操做add、remove、set、get、size等进行了统必定义,同时List实现了Collection接口,继续看下Collection接口的定义(先不关心Iterator):数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface Collection&lt;E&gt; extends Iterable&lt;E&gt; {
            int size();
      boolean isEmpty();
      boolean contains(Object o);
      Iterator&lt;E&gt; iterator();
      Object[] toArray();
      &lt;T&gt; T[] toArray(T[] a);
      boolean add(E e);
      boolean remove(Object o);
     boolean containsAll(Collection&lt;?&gt; c);
     boolean addAll(Collection&lt;? extends E&gt; c);
     boolean removeAll(Collection&lt;?&gt; c);
     boolean retainAll(Collection&lt;?&gt; c);
     void clear();
     boolean equals(Object o);
     int hashCode();
}<span style= "font-weight: normal;" >  </span>

有了这两个接口,对于ArrayList和LinkeList的操做是否是就能够这么写了呢?数据结构

1
2
3
4
Collection<String> collection = new ArrayList<String>();
collection.add( "hello" );
collection.add( "java" );
collection.remove( "hello" );

对于ArrayList的实现不满意,ok换成LinkedList实现,多线程

1
2
3
4
Collection<String> collection = new LinkedList<String>();
collection.add( "hello" );
collection.add( "java" );
collection.remove( "hello" );

对于用户来讲,add、remove等操做是没有任何影响的,好了,到这里了解了统一接口,面向接口编程的好处,接下来在思考另一个问题,怎么给容器提供一种遍历方式。dom

 

2.Iterator设计思想
 
     你可能会认为不就是遍历嘛,很好提供啊,ArrayList就用数组下标进行遍历,LinkedList就用指针next进行遍历,仔细想一下真的这么简单嘛?结合上一个问题,若是底层的容器实现变化了,用户的使用是否是也须要根据具体实现的数据结构来改变遍历方式呢?显然这样是不科学的吗,这么简单我讲上面的统一接口干吗,对不对。。。
 
     是的,须要统一一种遍历方式,提供一个统计遍历接口,而具体实现须要具体的容器本身根据自身数据结构特色来完成,而对于用户就只有一个接口而已,万年不变。因而就有了Iterator,接下来看下Iterator的实现吧。。。
     先回过头去看Collection的定义,发现Collection 实现了一个Iterable 接口(早就发现了好嘛?),来看下的Iterable的定义,
 
1
2
3
4
5
6
7
8
9
10
11
12
13
/** Implementing this interface allows an object to be the target of
   *  the "foreach" statement.
   * @since 1.5
   */
  public interface Iterable<T> {
 
      /**
       * Returns an iterator over a set of elements of type T.
       *
      * @return an Iterator.
      */
     Iterator<T> iterator();
}

英文注释说,实现iterable接口的类可使用“foreach”操做,而后并要求实现Iterator<T> iterator()方法,该方法返回一个Iterator接口对象,来看下Iterator接口,ide

1
2
3
4
5
6
7
8
public interface Iterator<E> {
     // 是否还有元素
     boolean hasNext();
     // 下一个元素
     E next();
     // 将迭代器返回的元素删除
     void remove();
}

Iterator接口一共有三个方法,经过这三个方法就能够实现通用遍历了,ok,咱们来试一下。学习

1
2
3
4
5
6
7
8
Collection<String> collection = new ArrayList<String>();
collection.add( "hello" );
collection.add( "java" );
 
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
      System. out.println(iterator.next());
}
  用户不再用关心容器的底层实现了,无论你是数组仍是链表仍是其余什么,只须要用Iterator就能够进行遍历了。
 
     到这里Iterator的设计原则和思路实际上已经讲完了,咱们来整理下Iterator模式的几个角色:
 
     1.迭代器Iterator接口。
     2.迭代器Iterator的实现类,要求重写hasNext(),next(),remove()三个方法 。
     3.容器统一接口,要求有一个返回Iterator接口的方法。
     4.容器实现类,实现一个返回Iterator接口的方法。

 

3.本身实现容器的Iterator遍历
 
     若是让咱们本身来实现Iterator模式,应该怎么来实现呢,试一下。
  Iterator接口:
1
2
3
4
public interface MyIterator {
        Object next();
         boolean hasNext();
}

容器统一接口:this

1
2
3
4
5
public interface MyCollection {
         void add(Object o);
         int size();
        MyIterator iterator();
}

容器实现类和Iterator实现类(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
48
49
public class MyArrayList implements MyCollection {
 
          private Object[] data ;
          private int size;
 
          public MyArrayList() {
                 data = new Object[ 10 ];
         }
 
         public void add(Object o) {
                if (size == data. length) {
                      Object[] newData = new Object[data .length * 2 ];
                      System. arraycopy(data, 0 , newData, 0 , data.length     );
                       data = newData;
               }
                data[size ] = o;
                size++;
        }
 
         public int size() {
                return size ;
        }
 
         @Override
         public MyIterator iterator() {
                return new Itr();
        }
 
         private class Itr implements MyIterator {
                private int index = 0 ;
 
                @Override
                public boolean hasNext() {
                       if (index >= size) {
                             return false ;
                      } else {
                             return true ;
                      }
               }
 
                @Override
                public Object next() {
                      Object o = data[index ];
                       index++;
                       return o;
               }
 
        }
}

应用一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {
 
          public static void main(String[] args) {
                MyCollection c = new MyArrayList();
                c.add( "t" );
                c.add( "s" );
                c.add( "t" );
                c.add( "d" );
 
               System. out.println(c.size());
 
               MyIterator itr = c.iterator();
                while (itr.hasNext()) {
                      System. out.println(itr.next());
               }
        }
 
}
  看看结果:
1
2
3
4
5
4
t
s
t
d

没问题,很容易就实现了对不对,固然这里只是简单模拟,没有过多的追求效率和优雅的设计。

 

4.ArrayList的Iterator实现
 
     下面来看一看ArrayList和LinkedList对Iterator接口的实现吧,ArrayList的Iterator实现封装在AbstractList中,看一下它的实现:
1
2
3
public Iterator&lt;E&gt; iterator() {
              return new Itr();
       }<span style= "font-weight: normal;" > </span>

和本身实现的同样,采用内部类实现 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
48
49
50
51
52
53
54
55
56
57
58
private class Itr implements Iterator<E> {
         // 将要next返回元素的索引
          int cursor = 0 ;
         // 当前返回的元素的索引,初始值-1
          int lastRet = - 1 ;
 
          /**
          * The modCount value that the iterator believes that the backing
          * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
         int expectedModCount = modCount;
 
         public boolean hasNext() {
             // 因为cursor是将要返回元素的索引,也就是下一个元素的索引,和size比较是否相等,也就是判断是否已经next到最后一个元素
             return cursor != size();
        }
 
         public E next() {
             checkForComodification();
            try {
               // 根据下一个元素索引取出对应元素
               E next = get( cursor);
               // 更新lastRet为当前元素的索引,cursor加1
                lastRet = cursor ++;
               // 返回元素
                return next;
            } catch (IndexOutOfBoundsException e) {
               checkForComodification();
                throw new NoSuchElementException();
            }
        }
 
         public void remove() {
            // remove前必须先next一下,先取得当前元素
            if (lastRet == - 1 )
                throw new IllegalStateException();
             checkForComodification();
 
            try {
               AbstractList. this .remove(lastRet );
                // 确保lastRet比cursor小、理论上永远lastRet比cursor小1
                if (lastRet < cursor)
                   // 因为删除了一个元素cursor回退1
                   cursor--;
                // 重置为-1
                lastRet = - 1 ;
                expectedModCount = modCount ;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }
 
         final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
}
  ArrayList的Iterator实现起来和咱们本身实现的大同小异,很好理解。
     有一点须要指出的是这里的checkForComodification 检查,以前ArrayList中留了个悬念,modCount这个变量究竟是作什么的,这里简单解释一下:迭代器Iterator容许在容器遍历的时候对元素进行删除,可是有一点问题那就是当多线程操做容器的时候,在用Iterator对容器遍历的同时,其余线程可能已经改变了该容器的内容(add、remove等操做),因此每次对容器内容的更改操做(add、remove等)都会对modCount+1,这样至关于乐观锁的版本号+1,当在Iterator遍历中remove时,检查到modCount发生了变化,则立刻抛出异常ConcurrentModificationException,这就是Java容器中的 “fail-fast”机制
 
5.LinkedList的Iterator实现
 
     LinkedList的Iterator实现定义在AbstracSequentialtList中,实如今AbstractList中,看一下:
     AbstracSequentialtList的定义:
1
2
3
public Iterator<E> iterator() {
         return listIterator();
     }

AbstractList的实现:

1
2
3
4
5
6
7
8
9
public ListIterator<E> listIterator() {
         return listIterator( 0 );
     }
    public ListIterator<E> listIterator( final int index) {
         if (index< 0 || index>size())
          throw new IndexOutOfBoundsException( "Index: " +index);
 
         return new ListItr(index);
     }

看一下ListItr实现:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
   private class ListItr implements ListIterator&lt;E&gt; {
           // 最后一次返回的节点,默认位header节点
           private Entry&lt;E&gt; lastReturned = header;
           // 将要返回的节点
           private Entry&lt;E&gt; next ;
           // 将要返回的节点index索引
           private int nextIndex;
           private int expectedModCount = modCount;
 
         ListItr( int index) {
             // 索引越界检查
             if (index &lt; 0 || index &gt; size)
                 throw new IndexOutOfBoundsException( "Index: " +index+
                                               ", Size: " +size );
             // 简单二分,判断遍历的方向
             if (index &lt; (size &gt;&gt; 1 )) {
                 // 取得index位置对应的节点
                 next = header .next;
                 for (nextIndex = 0 ; nextIndex&lt;index; nextIndex++)
                    next = next .next;
             } else {
                 next = header ;
                 for (nextIndex =size; nextIndex&gt;index; nextIndex --)
                    next = next .previous;
             }
         }
 
          public boolean hasNext() {
             // 根据下一个节点index是否等于size,判断是否有下一个节点
             return nextIndex != size;
         }
 
          public E next() {
             checkForComodification();
             // 遍历完成
             if (nextIndex == size)
                 throw new NoSuchElementException();
 
             // 赋值最近一次返回的节点
             lastReturned = next ;
             // 赋值下一次要返回的节点(next后移)
             next = next .next;
             // 将要返回的节点index索引+1
             nextIndex++;
             return lastReturned .element;
         }
 
          public boolean hasPrevious() {
             return nextIndex != 0 ;
         }
 
         //  返回上一个节点(双向循环链表嘛、能够两个方向遍历)
          public E previous() {
             if (nextIndex == 0 )
                 throw new NoSuchElementException();
 
             lastReturned = next = next. previous;
             nextIndex--;
             checkForComodification();
             return lastReturned .element;
         }
 
          public int nextIndex() {
             return nextIndex ;
         }
 
          public int previousIndex() {
             return nextIndex - 1 ;
         }
 
          public void remove() {
              checkForComodification();
              // 取出当前返回节点的下一个节点
              Entry&lt;E&gt; lastNext = lastReturned.next ;
              try {
                  LinkedList. this .remove(lastReturned );
              } catch (NoSuchElementException e) {
                  throw new IllegalStateException();
              }
             // 确认下次要返回的节点不是当前节点,若是是则修正
             if (next ==lastReturned)
                  next = lastNext;
              else
               // 因为删除了一个节点,下次要返回的节点索引-1
                 nextIndex--;
             // 重置lastReturned为header节点
             lastReturned = header ;
             expectedModCount++;
         }
 
          public void set(E e) {
             if (lastReturned == header)
                 throw new IllegalStateException();
             checkForComodification();
             lastReturned.element = e;
         }
 
          public void add(E e) {
             checkForComodification();
            lastReturned = header ;
            addBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }
 
         final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
}<span style= "font-weight: normal;" > </span>
  LinkedList的Iterator实现就分析完了,从这里能够看出数组和链表的遍历方式不一样,但对于用户统一使用Iterator迭代器进行遍历器,作到只需面对接口编程,这也是Iterator的最终要作到的。
 
     最后啰嗦一句,Iterator是比较简单的一种设计模式,使用场景也仅限于此,不要纠结于Iterator的应用而是要明白其设计思想,同时要对一些经常使用数据结构应该要了解掌握。
 
     Iterator 完!
相关文章
相关标签/搜索