Java學習之Iterator(迭代器)的一般用法


 

一、迭代器概述

  1、什么是迭代器?

  在Java中,有很多的數據容器,對於這些的操作有很多的共性。Java采用了迭代器來為各種容器提供了公共的操作接口。這樣使得對容器的遍歷操作與其具體的底層實現相隔離,達到解耦的效果。

  在Iterator接口中定義了三個方法:

  

  2、迭代器使用

    public static void main(String[] args) { List<String> list=new ArrayList<>(); list.add("abc"); list.add("edf"); list.add("ghi"); for(Iterator<String> it=list.iterator();it.hasNext();) { System.out.println(it.next()); } }

 執行結果: 

二、ArrayList的Iterator實現

   private class Itr implements Iterator<E> 
  { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; ... }

  在ArrayList內部定義了一個內部類Itr,該類實現了Iterator接口。

  在Itr中,有三個變量分別是

  cursor:表示下一個元素的索引位置

  lastRet:表示上一個元素的索引位置

  expectModCount:預期被修改的次數

  下面看一下Itr類實現了Iterator接口的三個方法:

 public boolean hasNext() { return cursor != size;//當cursor不等於size時,表示仍有索引元素 }
     public E next() //返回下一個元素  { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; }

  在next()方法中有一個checkForComodification()方法,其實現為:

    final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }

  可以看到,該函數是用來判斷集合的修改次數是否合法。

  在集合內部維護一個字段modCount用於記錄集合被修改的次數,每當集合內部結構發生變化(add,remove,set)時,modCount+1。

  在迭代器內部也維護一個字段expectedModCount,同樣記錄當前集合修改的次數,初始化為集合的modCount值。當我們在調用Iterator進行遍歷操作時,如果有其他線程修改list會出現modCount!=expectedModCount的情況,就會報並發修改異常java.util.ConcurrentModificationException。下面為示例代碼:

   public static void main(String[] args) { ArrayList<String> aList=new ArrayList<String>(); aList.add("bbc"); aList.add("abc"); aList.add("ysc"); aList.add("saa"); System.out.println("移除前:"+aList); Iterator<String> it=aList.iterator(); while(it.hasNext()) { if("abc".equals(it.next())) { aList.remove("abc"); } } System.out.println("移除后:"+aList); } 

  

  上面的代碼中,如果我們只使用迭代器來進行刪除,則不會出現並發修改異常錯誤。

  public static void main(String[] args) {     ArrayList<String> aList=new ArrayList<String>(); aList.add("bbc"); aList.add("abc"); aList.add("ysc"); aList.add("saa"); System.out.println("移除前:"+aList); Iterator<String> it=aList.iterator(); while(it.hasNext()) { if("abc".equals(it.next())) { it.remove(); } } System.out.println("移除后:"+aList); }

  

     public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }

  在執行remove操作時,同樣先執行checkForComodification(),然后會執行ArrayList的remove()方法,該方法會將modCount值加1,這里我們將expectedModCount=modCount,使之保持統一。

三、ListIterator

  上面可以看到,Iterator只提供了刪除元素的方法remove,如果我們想要在遍歷的時候添加元素怎么辦?

  ListIterator接口繼承了Iterator接口,它允許程序員按照任一方向遍歷列表,迭代期間修改列表,並獲得迭代器在列表中的當前位置。

  ListIterator接口定義了下面幾個方法:

  

  下面使用ListIterator來對list進行邊遍歷邊添加元素操作:

    public static void main(String[] args) { ArrayList<String> aList = new ArrayList<String>(); aList.add("bbc"); aList.add("abc"); aList.add("ysc"); aList.add("saa"); System.out.println("移除前:" + aList); ListIterator<String> listIt = aList.listIterator(); while (listIt.hasNext()) { if ("abc".equals(listIt.next())) { listIt.add("haha"); } } System.out.println("移除后:" + aList); }

  

 

=============================================

迭代器(Iterator)

  迭代器是一種設計模式,它是一個對象,它可以遍歷並選擇序列中的對象,而開發人員不需要了解該序列的底層結構。迭代器通常被稱為“輕量級”對象,因為創建它的代價小。

  Java中的Iterator功能比較簡單,並且只能單向移動:

  (1) 使用方法iterator()要求容器返回一個Iterator。第一次調用Iterator的next()方法時,它返回序列的第一個元素。注意:iterator()方法是java.lang.Iterable接口,被Collection繼承。

  (2) 使用next()獲得序列中的下一個元素。

  (3) 使用hasNext()檢查序列中是否還有元素。

  (4) 使用remove()將迭代器新返回的元素刪除。

  Iterator是Java迭代器最簡單的實現,為List設計的ListIterator具有更多的功能,它可以從兩個方向遍歷List,也可以從List中插入和刪除元素。

迭代器應用:
 list l = new ArrayList();
 l.add("aa");
 l.add("bb");
 l.add("cc");
 for (Iterator iter = l.iterator(); iter.hasNext();) {
  String str = (String)iter.next();
  System.out.println(str);
 }
 /*迭代器用於while循環
 Iterator iter = l.iterator();
 while(iter.hasNext()){
  String str = (String) iter.next();
  System.out.println(str);
 }
 */

 

=========================================================

 

 

 

Java迭代器深入理解及使用

 

轉載 2015年08月22日 01:21:42

 

 

 

Iterator(迭代器)

            作為一種設計模式,迭代器可以用於遍歷一個對象,對於這個對象的底層結構開發人員不必去了解。

       java中的Iterator一般稱為“輕量級”對象,創建它的代價是比較小的。這里筆者不會去考究迭代器這種

       設計模式,僅在JDK代碼層面上談談迭代器的時候以及使用迭代器的好處。

Iterator詳解

            Iterator是作為一個接口存在的,它定義了迭代器所具有的功能。這里我們就以Iterator接口來看,不考

       慮起子類ListIterator。其源碼如下:      

 

[java] view plain copy
  1. package java.util;    
  2. public interface Iterator<E> {    
  3.     boolean hasNext();    
  4.     E next();    
  5.     void remove();    
  6. }    


            對於這三個方法所實現的功能,字面意義就是了。不過貌似對迭代器的工作“過程”還是迷霧,接下來

 

         我們以一個實際例子來看。

 

[java] view plain copy
  1. List<String> list = new ArrayList<String>();    
  2.         list.add("TEST1");    
  3.         list.add("TEST2");    
  4.         list.add("TEST3");    
  5.         list.add("TEST4");    
  6.         list.add("TEST6");    
  7.         list.add("TEST5");    
  8.         Iterator<String> it = list.iterator();     
  9.         while(it.hasNext())    
  10.         {    
  11.             System.out.println(it.next());    
  12.         }    


                這段代碼的輸出結果不用多說,這里的it更像是“游標”,不過這游標具體做了啥,我們還得通過

 

           list.iterator()好好看看。通過源碼了解到該方法產生了一個實現Iterator接口的對象。

 

[java] view plain copy
  1. private class Itr implements Iterator<E> {    
  2.           
  3.        int cursor = 0;    
  4.        int lastRet = -1;    
  5.        int expectedModCount = modCount;    
  6.        public boolean hasNext() {    
  7.            return cursor != size();    
  8.        }    
  9.     
  10.        public E next() {    
  11.            checkForComodification();    
  12.            try {    
  13.                int i = cursor;    
  14.                E next = get(i);    
  15.                lastRet = i;    
  16.                cursor = i + 1;    
  17.                return next;    
  18.            } catch (IndexOutOfBoundsException e) {    
  19.                checkForComodification();    
  20.                throw new NoSuchElementException();    
  21.            }    
  22.        }    
  23.     
  24.        public void remove() {    
  25.            if (lastRet < 0)    
  26.                throw new IllegalStateException();    
  27.            checkForComodification();    
  28.     
  29.            try {    
  30.                AbstractList.this.remove(lastRet);    
  31.                if (lastRet < cursor)    
  32.                    cursor--;    
  33.                lastRet = -1;    
  34.                expectedModCount = modCount;    
  35.            } catch (IndexOutOfBoundsException e) {    
  36.                throw new ConcurrentModificationException();    
  37.            }    
  38.        }    
  39.     
  40.        final void checkForComodification() {    
  41.            if (modCount != expectedModCount)    
  42.                throw new ConcurrentModificationException();    
  43.        }    
  44.    }    


                     對於上述的代碼不難看懂,有點疑惑的是int expectedModCount = modCount;這句代碼

 

             其實這是集合迭代中的一種“快速失敗”機制,這種機制提供迭代過程中集合的安全性。閱讀源碼

             就可以知道ArrayList中存在modCount對象,增刪操作都會使modCount++,通過兩者的對比

             迭代器可以快速的知道迭代過程中是否存在list.add()類似的操作,存在的話快速失敗!

                     以一個實際的例子來看,簡單的修改下上述代碼。        

 

[java] view plain copy
  1. while(it.hasNext())    
  2.         {    
  3.             System.out.println(it.next());    
  4.             list.add("test");    
  5.         }    


                      這就會拋出一個下面的異常,迭代終止。

 

          

                       對於快速失敗機制以前文章中有總結,現摘錄過來:    

Fail-Fast(快速失敗)機制

 

                     仔細觀察上述的各個方法,我們在源碼中就會發現一個特別的屬性modCount,API解釋如下:

            The number of times this list has been structurally modified. Structural modifications are those

             that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress

             may yield incorrect results.

              記錄修改此列表的次數:包括改變列表的結構,改變列表的大小,打亂列表的順序等使正在進行

          迭代產生錯誤的結果。Tips:僅僅設置元素的值並不是結構的修改

              我們知道的是ArrayList是線程不安全的,如果在使用迭代器的過程中有其他的線程修改了List就會

             拋出ConcurrentModificationException這就是Fail-Fast機制。   

                 那么快速失敗究竟是個什么意思呢?

          在ArrayList類創建迭代器之后,除非通過迭代器自身remove或add對列表結構進行修改,否則在其他

          線程中以任何形式對列表進行修改,迭代器馬上會拋出異常,快速失敗。 

迭代器的好處

           通過上述我們明白了迭代是到底是個什么,迭代器的使用也十分的簡單。現在簡要的總結下使用迭代

       器的好處吧。

                1、迭代器可以提供統一的迭代方式。

                2、迭代器也可以在對客戶端透明的情況下,提供各種不同的迭代方式。

                3、迭代器提供一種快速失敗機制,防止多線程下迭代的不安全操作。

           不過對於第三點尚需注意的是:就像上述事例代碼一樣,我們不能保證迭代過程中出現“快速

         失敗”的都是因為同步造成的,因此為了保證迭代操作的正確性而去依賴此類異常是錯誤的!

 foreach循環

           通過閱讀源碼我們還發現一個Iterable接口。它包含了一個產生Iterator對象的iterator()方法,

       而且將Iterator對象唄foreach用來在序列中移動。對於任何實現Iterable接口的對象都可以使用

       foreach循環。

           foreach語法的冒號后面可以有兩種類型:一種是數組,另一種是是實現了Iterable接口的類

        對於數組不做討論,我們看看實現了Iterable的類

 

[java] view plain copy
  1. package com.iterator;    
  2.     
  3. import java.util.Iterator;    
  4.     
  5. public class MyIterable implements Iterable<String> {    
  6.     protected String[] words = ("And that is how "    
  7.            + "we know the Earth to be banana-shaped.").split(" ");    
  8.      
  9.     public Iterator<String> iterator() {    
  10.        return new Iterator<String>() {    
  11.            private int index = 0;    
  12.      
  13.            public boolean hasNext() {    
  14.               return index < words.length;    
  15.            }    
  16.      
  17.            public String next() {    
  18.               return words[index++];    
  19.            }    
  20.      
  21.            public void remove() {}    
  22.        };    
  23.     }    
  24.        
  25.     public static void main(String[] args){    
  26.        for(String s:new MyIterable())    
  27.            System.out.print(s+",");    
  28.     }   

   輸出結果如下:

 

                  And,that,is,how,we,know,the,Earth,to,be,banana-shaped.,

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM