1. 程式人生 > >迭代器 Iterator 總結

迭代器 Iterator 總結

今天一位朋友在用迭代器時很鬱悶為什麼會會報Java.util.ConcurrentModificationExceptiond異常, 於是寫下這篇部落格,想詳細的講講Java裡面  的迭代器.

           Iterator簡單的來說就是遍歷, 遍歷什麼? 遍歷集合元素等.  

          Iterator介面共有四個方法:

public interface Iterator<E>{

       boolean hasNext():如果被迭代的集合還元素沒有被遍歷,則返回true。
    Object next():返回集合裡下一個元素。
    void remove() :刪除集合裡上一次next方法返回的元素
    void forEachRemaining(Consumer action),這是Java 8為Iterator新增的預設方法,該方法可使用Lambda表示式來遍歷集合元素。
}  
}

 如果只是普通的迭代輸出,那麼不會出現什麼問題.但是在現實需求中這往往沒有什麼用,我們還需要做一些額外的操作,比如說

 add/remove等。 但是這樣做很可能會出現一些很尷尬的事情, 下面我給出一段正確的迭代刪除程式碼:

import java.util.*;

public class TestIterator
{
    public static void main(String[] args)
    {
        // 建立集合、新增元素
        Collection<String> books = new ArrayList();
        books.add("測試1");
        books.add("測試2");
        books.add("測試3");
   
        Iterator it = books.iterator();
        while(it.hasNext())
        {
            // it.next()方法返回的資料型別是Object型別,因此需要強制型別轉換
            String book = (String)it.next();
            System.out.println(book);
            if (book.equals("測試2"))
            {
                // 從集合中刪除上一次next方法返回的元素
                it.remove();
            }

        }
        System.out.println(books);
    }
}

上面的程式碼經過測試是可以正確刪除元素的,有疑問的程式碼估計也就是這一句了
  Iterator it = books.iterator();
這段程式碼的意思是 獲得List物件的迭代器,然後通過迭代器來遍歷List物件內儲存的元素. 接著我們就可以呼叫他的 hasNext方法和next方法去操作他了. 接著貼出一段錯誤的程式碼這是在我工作機上面測試的程式碼 jdk是1.8 (當然我相信以前的jdk結果應該也是這樣):   那麼為什麼二種刪除方式一個有錯一個沒錯呢(這個問題難倒了工作三年的一個朋友).  經過剛剛的測試,我們發現用迭代器本身刪除是可以的,但是在迭代器中

對list本身進行刪除就不行了, 對於這種莫名其妙的問題,我們從原始碼入手, 首先根據上面的我們先看看ArrayList他是怎麼返回Iterator例項的,最終原始碼如下:


 在ArrayList類中有一個私有類Private Itr 我們注意到expectedModCount這個欄位,他的初始值是等於modCount的。 那modCount又是幹啥的呢 最直接的。 看原始碼:

 

我們從原始碼可以對集合的每一次 add / remove 都會觸發一下這個欄位,而上面Itr的remove 不管在刪除還是新增都會觸發一個方法(csdn上截圖太麻煩我直接寫了 )   checkForComodification();這個方法, 大家可以看看上面的截圖而這個 checkForComodification();方法中只有一個功能

 if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

 那就是丟擲異常~~~~

所以大家應該懂了那段錯誤的程式碼為什麼會錯誤了吧。。 因為二個欄位不相等啊。 沒有同步啊。

如果你問我為什麼要這麼做,我只能這麼跟你說:

Iterator 是工作在一個獨立的執行緒中,並且擁有一個 mutex 鎖。 Iterator 被建立之後會建立一個指向原來物件的單鏈索引表,當原來的物件數量發生變化時,這個索引表的內容不會同步改變,所以當索引指標往後移動的時候就找不到要迭代的物件,所以按照 fail-fast 原則 Iterator 會馬上丟擲 java.util.ConcurrentModificationException 異常。
所以 Iterator 在工作的時候是不允許被迭代的物件被改變的。但你可以使用 Iterator 本身的方法 remove() 來刪除物件, Iterator.remove() 方法會在刪除當前迭代物件的同時維護索引的一致性