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 完!
相關文章
相關標籤/搜索