ListIterator源碼逐條解析

一家之言 姑妄言之 絮絮叨叨 不足爲訓

ListIterator接口註釋翻譯:

   列表的迭代器,它允許程序員以任意方向遍歷列表,在迭代期間修改列表,並獲取迭代器在列表中的當前位置。ListIterator沒有當前元素。它的遊標位置總是位於調用previous()返回的元素和調用next()返回的元素之間。長度爲n的列表的迭代器有n+1個可能的光標位置,如下面的插入符號(^)所示:

Element(0) Element(1) Element(2) Element(3) Element(n-1)
^ ^ ^ ^ ^

   注意,remove和set(object)方法不是根據遊標位置定義的。它們被定義爲對調用next()或previous()返回的最後一個元素進行操作。
   該接口是Java集合框架的成員。


筆者廢話:

   這個接口的註釋說的我也是雲裏霧裏,但是我們大概可以從中略窺一二。首先它說明了我們這個接口是可以從任意方向遍歷的。如果你仔細看過這個接口內的方法聲明,也就明白了這個“任意方向”的含義。通俗來說,它有了所謂的“前驅節點”和“後繼節點”。
   我相信看到這兩個名詞你可能就明白了這個接口的本意,它就是一個雙向鏈表,不過我們平常也叫它雙鏈表
   其次,它也告訴我們這個接口的遊標是不指向元素的,它位於前驅節點後繼節點之間。不過我還是要說,前驅節點後繼節點之間不是本身還有元素嗎?我實在不理解這個作者在寫這段註釋的含義。但是不可否認的是,它確實沒有當前元素。這個還是通過聲明的方法就可以得出的結論。


ListIterator接口信息:

public interface ListIterator<E> extends Iterator<E>

   我們可以清楚的看到,ListIterator是一個接口,而且這裏還規定了泛型機制。同時,這個接口繼承了Iterator接口,關於Iterator接口的解析請參照Iterator源碼逐條解析這篇文章吧。


ListIterator接口方法信息:

/**
 * 如果此列表迭代器在正向遍歷列表時包含更多元素,則返回true。
 * (換句話說,如果next將返回一個元素而不是拋出一個異常,則返回true。)
 *
 * @return 如果在正向遍歷列表時,列表迭代器有更多的元素則返回true
 */
boolean hasNext();

   這裏覆寫了Iterator接口中的hasNext()方法。從註釋上來說它更多地描述了一個正向遍歷的概念,當然,這是因爲我們這個接口還有反向遍歷的方法hasPrevious()。其餘的解釋和Iterator源碼逐條解析裏說的一樣,不過我還是複製到下面供參考。不過如果已經閱讀了我提到的這篇Iterator源碼逐條解析文章,那麼下面兩段忽略即可。
   這個方法意在詢問當前遍歷的容器中是否含有下一個元素。這裏會涉及到一個指針操作,當然,我們java宣稱的可是沒有指針,所以,這我們可以把它稱之爲“指向”。一般來說,如果我們使用了某個集合的ListIterator對象後,會在這個集合的上方出現一個空值,而我們的ListIterator對象就指向這個空值
   但是需要注意的是,這個方法僅僅是詢問下一個元素是否存在,此時並不移動指向位置,只有調用了next()方法後,這個指向位置纔會進行移動,即,移動到下一項。

/**
 * 返回列表中的下一個元素並向前移動光標位置。可以重複調用此方法來
 * 遍歷列表,也可以將此方法與對previous的調用混合在一起來回調用。
 * (注意,交替調用next和previous將重複返回相同的元素。)
 *
 * @return the next element in the list
 * @throws NoSuchElementException if the iteration has no next element
 */
E next();

   這裏覆寫了Iterator接口中的next()方法。我們從註釋上可以看到,這個方法可以和previous()方法重複使用。不過這裏也說明了如果交替使用的話將返回相同的元素。這個倒是容易理解,你遍歷前又遍歷後,便利後又遍歷前,一直魔力轉圈圈。那麼以下兩段依舊引自Iterator源碼逐條解析中的解析。如無疑問,那麼就跳過。
   這個方法是與hasNext()方法遙相呼應的。它代表着返回迭代器中的下一個元素。一般來說,我們都會通過hasNext()方法來判斷是否有下一個元素,一旦有,那麼就調用next()方法展示(取出)這個元素。
   重複上面的一段話:調用了next()方法後,容器的指向位置會進行移動,即,移動到下一項。

/**
 * 如果該列表迭代器在反向遍歷列表時包含更多元素,則返回true。
 * (換句話說,如果previous返回一個元素而不是拋出一個異常,則返回true。)
 *
 * @return 當以反向遍歷列表時,如果列表迭代器有更多的元素則返回true
 */
boolean hasPrevious();

   好的,這個是我們ListIterator接口定義的一個新方法。它和hasNext()方法是對應的。一個是正向遍歷(hasNext()),一個是反向遍歷(hasPrevious())。
   這個方法意在詢問當前遍歷的容器中是否含有上一個元素。它依舊會存有一個指向元素的指向。不過它和hasNext()方法一樣,僅僅是詢問上一個元素是否存在,此時並不移動指向位置,只有調用了previous()方法後,這個指向位置纔會進行移動,即,移動到上一項。

/**
 * 返回列表中的前一個元素,並將光標向後移動。可以反覆調用此方法以
 * 向後遍歷列表,也可以將此方法與對next的調用混合在一起來回調用。
 * (注意,交替調用next和previous將重複返回相同的元素。)
 *
 * @return 返回列表中的前一個元素
 * @throws 如果迭代沒有先前的元素則拋出NoSuchElementException異常
 */
E previous();

   這個是我們ListIterator接口定義的一個新方法。我們從註釋上可以看到,這個方法可以和next()方法重複使用。不過,需要注意的是,註釋中在解釋這個方法的時候使用了backwards,即向後遍歷。其實就是我們通常意義上的向前遍歷(向左移動)。這裏不是翻譯的問題,而是理解性的問題。那麼以下兩段仿照Iterator源碼逐條解析中的解析。如無疑問,那麼就跳過。
   這個方法是與hasPrevious()方法遙相呼應的。它代表着返回迭代器中的上一個元素。一般來說,我們都會通過hasPrevious()方法來判斷是否有上一個元素,一旦有,那麼就調用previous()方法展示(取出)這個元素。
   重複上面的一段話:調用了previous()方法後,容器的指向位置會進行移動,即,移動到上一項。

/**
 * 返回將由後續調用next返回的元素的索引。(如果列表迭代器位於列表的末尾,
 * 則返回列表大小。)
 *
 * @return 對next的後續調用將返回的元素的索引,或者如果列表迭代器位於
 * 列表末尾,則爲列表大小
 */
int nextIndex();

   這個是我們ListIterator接口定義的一個新方法。這個方法在註釋中說是“返回將由後續調用next返回的元素的索引”,實際上它返回當前元素的遊標,也就是當前元素的索引值(這個元素可還沒遍歷過哦)。通俗來說,當我們獲取到需要遍歷集合的ListIterator時,這個時候我們的遊標cursor肯定指向了0。那麼這個nextIndex()返回的就是0。記住,這裏返回的,可是未遍歷過得元素索引,因爲我們的例子中並沒有提到調用next()後再調用nextIndex()

/**
 * 返回將由後續調用previous返回的元素的索引。(如果列表迭代器位於列表的開頭,
 * 則返回-1。)
 *
 * @return 對previous後續調用將返回的元素的索引,如果列表迭代器位於
 * 列表開頭,則爲-1
 */
int previousIndex();

   這個方法的解釋其實和nextIndex()相似,因爲其功能相反。它返回的就是當前元素遊標的前一項。可能有些許繞。也就是說,它返回的是我們當前最近一次遍歷過的元素的索引值。也就是這個方法的使用,起碼在第一次獲取到ListIterator時,必須先進行next()行爲,使得我們的遊標cursor0變爲1,然後再調用previousIndex()方法才能返回0這個索引,否則則返回-1

/**
 * 從列表中刪除next或previous返回的最後一個元素(可選操作)。此調用
 * 在每次調用next或previous時只能執行一次。只有在最後一次調用next
 * 或previous之後沒有調用add時,纔可以執行此操作。
 *
 * @throws 如果此列表迭代器不支持刪除操作則拋出
 * UnsupportedOperationException異常
 * @throws 如果沒有調用next或previous,或者在最後一次調用next或
 * previous之後調用了remove或add則拋出IllegalStateException異常
 */
void remove();

   這個remove()方法挺有意思。
   首先,這個方法只有在調用了next()或者previous()方法之後纔可調用。而且你中途還不能調用add(E e)操作。這個不難理解,一般我們在調用了next()或者previous()方法之後遊標進行變動。而刪除的就是跟這個遊標cursor相關的所在位置的元素。當然,我們這裏也不確定是哪一個,因爲這畢竟是個接口說明,還未到實現本接口的具體實現。所以這裏我們只能進行猜測。
   不過通過這種亦步亦趨的操作屬性,我們也能得出這裏最好不要進行添加行爲。因爲你可能破壞了當前的數據結構,造成刪除錯誤的結果
   其次,當我們沒有調用next()方法或previous()方法的話,或者最後一次調用next()方法或previous()方法之後調用了remove()方法或add(E e)方法會拋出IllegalStateException異常。關於這個解釋我感覺還是看相關實現類的實現要好。因爲前半句易理解,如果你不調用next()方法或previous()方法的話,遊標不移動它都不知道刪誰。但後半句卻不是那麼容易理解。所以,我們還是看具體方法的實現吧(放心,這個可不是一個小小接口說明就能搞清楚的~

/**
 * 將next或previous返回的最後一個元素替換爲指定的元素(可選操作)。
 * 只有在最後一次調用next或previous之後既不調用remove也不調用add,
 * 纔可以執行此調用。
 *
 * @param e 用來替換next或previous返回的最後一個元素的元素
 * @throws 如果此列表迭代器不支持set操作則拋出
 * UnsupportedOperationException異常
 * @throws 如果指定元素的類阻止將其添加到此列表中則拋出
 * ClassCastException異常
 * @throws 如果指定元素的某些方面阻止將其添加到此列表中則拋出
 * IllegalArgumentException異常
 * @throws 如果沒有調用next或previous,或者在最後一次調用next
 * 或previous之後調用了remove或add則拋出IllegalStateException異常
 */
void set(E e);

   這個方法的註釋讓我百思不得其解,但是這個set(E e)方法的本意我們還是可以大致弄清楚的,就是當你調用了next()方法或者previous()方法之後,如果你用了本方法,那麼就會修改當時你獲取的這個元素的值。譬如我們有一個數組:[“a” , “b” , “c” , “d”]。
   當我們初始化拿到Iterator的時候,開始使用next()方法,這個時候返回的原始應該是"a"吧?對,然後你再調用set("c")。這個時候你的數組就變成了:[“c” , “b” , “c” , “d”]。同理,調用previous()也是這種情形。
   具體的,我們還是看其實現類是如何進行操作的。

/**
 * 將指定的元素插入列表(可選操作)。元素被插入到next將返回的元素之前(如果有的話),
 * 以及previous將返回的元素之後(如果有的話)。(如果列表不包含元素,則新元素將成爲
 * 列表中惟一的元素。)新元素被插入到隱式遊標之前:對next的後續調用不受影響,而對
 * previous的後續調用將返回新元素。(這個調用將增加一個調用nextIndex或
 * previousIndex返回的值。)
 *
 * @param e 要插入的元素
 * @throws 如果此列表迭代器不支持add方法則拋出
 * UnsupportedOperationException異常
 * @throws 如果指定元素的類阻止將其添加到此列表中則拋出
 * ClassCastException異常
 * @throws 如果此元素的某些方面阻止將其添加到此列表中則拋出
 * IllegalArgumentException異常
 */
void add(E e);

   這個方法與其說是“添加”,我覺得不如說是“插入”來的更貼切些。
   它本意是要在調用了next()方法之後進行插入。抑或是在調用了previous()方法之後進行插入。放心,你沒有看錯。都是在之後,在哪個之後呢?在你調用了next()方法或previous()方法返回的元素之後。
   我們拿next()方法舉例
   首先我們有一個容器(不一定是數組):

1 2 3 4 5

   當我們兩次調用next()方法之後,你所返回的元素應該是下面^所指向的那個:

1 2 3 4 5 6
^

   這個時候你開始利用add(E e)方法進行插入,比如add(10)。它會插入到哪裏呢?它會插入下面這個地方:

1 2 3 4 5
^

   最後你得到的結果是這樣的:

1 2 10 3 4 5
^

   我們再拿previous()方法舉例
   首先我們有一個容器(不一定是數組):

1 2 3 4 5

   當我們三次調用next()方法之後緊接着調用一次previous()方法。你所返回的元素應該是下面^所指向的那個:

1 2 3 4 5 6
^

   這個時候你開始利用add(E e)方法進行插入,比如add(10)。它會插入到哪裏呢?它會插入下面這個地方:

1 2 3 4 5
^

   最後你得到的結果是這樣的:

1 2 10 3 4 5
^

   你會發現,這個方法無論在哪個場景下使用,都會添加到返回元素的後面。但是並不代表註釋翻譯的不正確,它儘量的描述類使用該方法的區別。只不過我們是從現象上來確認這種情況。
   OK,這些具體的實現方法我會在其具體的實現類裏面具體做介紹,這裏還是不摻雜那麼多的解釋或舉例。僅僅留給這個ListIterator接口一個說一是一的解析吧。
   至此,我們ListIterator到此全部解析完畢(ಥ_ಥ)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章