小白學Java:迭代器原來是這麼回事

小白學Java:迭代器原來是這麼回事

前文傳送門:Enumeration
上一篇,咱們談到了那個古老的迭代器Enumeration,還談到了取代他的新迭代器——Iterator。相比於以往,這個新物種又有哪些優勢呢?java

迭代器這個詞,在沒查找許多資料以前,我只知道個大概,我知道它能夠用來遍歷集合,可是至於它其中的奧妙,並無作深究。本篇文章關於Iterator迭代器作了小小的總結,鞏固學習,若是有理解錯誤,或敘述不當之處,還望你們評論區批評指針。編程

迭代器概述

官方文檔對Iterator的解釋是:設計模式

  • 它取代了Enumeration。
  • 它做用於任何一個Collection。
  • 它增長了remove的功能。
  • 它優化了方法命名。

不行不行,這描述也太簡略了,我繼續查找資料:ide

  • 迭代器自己是個對象,建立迭代器的代價很小,一般被稱爲輕量級對象
  • 迭代器其實也是一種設計模式,它提供了一種方法順序訪問一個聚合對象中的各個元素,但又不暴露該對象的內部表示。

迭代器設計模式

針對以上種種,我充滿了好奇,因而在複雜的繼承關係裏畫了又畫,最終才漸漸理清集合中所謂迭代器模式的體現,暫時以ArrayList爲例:
學習

  • 定義了一個迭代器的接口Iterator,裏面定義了迭代器的功能,但並無提供實現。
  • 定義了一個彙集接口Iterable,裏面的iterator()抽象方法,代表返回一個針對類型T的迭代器。此時將Iterable和Iterator聯繫了起來。
  • 咱們知道彙集接口被許多接口所擴展,定義相同的方法,Collection接口就是其一,因此說,迭代器針對於全部集合都有效。
  • 咱們以集合的具體類ArrayList爲例,暫時忽略之間的繼承關係,ArrayList顯然提供了抽象方法iterator()的具體實現,咱們查看源碼發現,它的返回值是一個Itr對象。
  • 這個Itr實際上是ArrayList的一個內部類,它提供了迭代器接口的具體實現(固然不必定是內部類),這樣全部東西都聯繫在了一塊兒。

Iterator定義的方法

  • hasNext():boolean 判斷下一個元素還有沒有,有就是true。
  • next(): E 返回序列中的下一個元素。

經過查看源碼,我發現,在這個Itr這個實現類中,定義了兩個指針:cursorlastRet。(還有個屬性爲expectedModCount初始化爲ArrayList的版本號modCount,這部分與fail-fast機制相關,以後會再提)而cursor初始爲0,與專門用來和集合元素數目size作比較的。而lastRet初始化爲-1,若是成功執行next操做,將會加1變成0,也就是上面說的「下一個元素」可想而知,能夠把lastRet認爲是初始化爲第一個元素以前的指針,和將要返回的值的索引相同,這樣會好記一些。測試

除了上面兩個方法,JDK1.8新增了兩個方法,也是體現處它與老迭代器不一樣的新優點:支持了刪除操做。優化

  • remove():void 將新近返回的元素刪除。
    須要注意的是:remove方法沒有新近返回的元素,也就是說lastRet<0,會拋出異常。若是移除成功,讓cursor往回退一格,lastRet重置爲-1。debug

  • forEachRemaining(Consumer<? super E> consumer):void 這個是JDK1.8中Iterator新增的默認方法:對剩餘的元素執行指定的操做
    可能不太好理解:咱們經過測試來講明一下:設計

List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    //建立一個Iterator對象
    Iterator<Integer> it = list.iterator();
    //返回第一個值
    System.out.print(it.next()+" ");

測試結果很明顯,只輸出了第一個元素:1。
咱們繼續在原代碼的基礎上咱們的新方法:

it.forEachRemaining(new Consumer<Integer>() {
        @Override
        public void accept(Integer integer) {
            System.out.print(integer+" ");
        }
    });

測試結果爲:1 2 3,在原來的基礎上,把剩下的元素都打印了出來。而這個新增的方法,其實和咱們熟悉的這個是同樣的:

while(it.hasNext()){
        System.out.print(it.next()+" ");
    }

值得一提的是:咱們以前學習的加強for循環,在底層其實就是運用了Iterator,我經過IDE的debug調試功能,發如今調用運行到加強for循環時,自動調用了集合的iterator()方法,返回了一個Iterator的實現類實例。

迭代器:統一方式

經過對Iterator中定義方法的學習,咱們大概知道了迭代器的用途,就是從前向後一個一個遍歷元素,而無視其內部結構。欸,遍歷我都懂,可無視結構在哪裏體現啊?別急,下面來看一個例子,讓咱們無視兩個不一樣集合的結構:

首先咱們定義一個方法,它能夠接收一個迭代器對象:

public static void display(Iterator<?> T){
        while(T.hasNext()){
            System.out.print(T.next());
        }
    }

而後咱們建立兩個不同的集合,一個是ArrayList,一個是HashSet,自己是無序的,咱們接下來應該會作相應的源碼學習。

//ArrayList 有序
        List<String> list = new LinkedList<>();
        list.add("天");
        list.add("喬");
        list.add("巴");
        list.add("夏");
        //HashSet 無序
        Set<Integer> set = new HashSet<>();
        set.add(11);
        set.add(22);
        set.add(33);
        set.add(44);
        display(list.iterator());//天 喬 巴 夏
        System.out.println();
        display(set.iterator());//33 22 11 44

能夠看出來,兩個不一樣集合的迭代器傳入display方法以後,都能用一種相同的方式訪問集合中的元素。
經過上面的一頓分析,咱們能夠肯定,迭代器這玩意兒,統一了訪問容器的方式

Iterator的總結

  • Iterator支持從前向後順次遍歷,統一了對不一樣集合裏元素的操做
  • 還在Enumeration的基礎上,簡化了命名,並且Enumeration並非對全部集合都適用。
  • 四大技能增刪改查,雖然支持刪和查,但不支持增和改。
  • 支持單向迭代,某些狀況下不是很靈活。(ListIterator能夠支持雙向,但只支持List類型)

最後,關於迭代器,還有一部份內容,在往後會作總結。 參考資料:《大話設計模式》、《Java編程思想》

相關文章
相關標籤/搜索