1. 程式人生 > 實用技巧 >java 迭代器

java 迭代器

一 概念

迭代器是一個物件,它的工作是遍歷並選擇序列中的物件,它提供了一種訪問一個容器物件中的各個元素的方法,而不必暴露容器物件的內部細節。

作用:

1 通過迭代器,開發人員不需要了解容器結構,就可以遍歷容器元素。被稱為輕量級容器(建立迭代器代價很小)

2 它的特點是更加安全,因為它可以確保,在當前遍歷的集合元素被更改的時候,就會丟擲ConcurrentModificationException異常

二 用法

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

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

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

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

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

簡單例子

import java.util.*;
public class Muster {
 
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("a");
        list.add("b");
        list.add("c");
        Iterator it 
= list.iterator(); while(it.hasNext()){ String str = (String) it.next(); System.out.println(str); } } }
View Code

三 迭代器是失效問題(刪除或新增元素)

在建立迭代器之後,除非通過迭代器自身的removeadd方法從結構上對列表進行修改,否則在任何時間以任何方式對列表進行修改,迭代器都會丟擲ConcurrentModificationException

public class ArrayListTest {
    
public static void main(String args[]) { List<String> strList = new ArrayList<String>(); //迭代器 Iterator<String> iterator = strList.iterator(); //修改了集合 for (int i = 0; i < 10; i++) { strList.add("string" + i); } while (iterator.hasNext()) { System.out.println(iterator.next()); } }

執行該段程式碼,會發現其丟擲如下異常:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at com.xyh.collection.ArrayListTest.main(ArrayListTest.java:21)
原因在於在迭代器建立之後,通過ArrayList自身的add方法對列表進行了修改,導致迭代器失效。當將藍色建立迭代器的程式碼移動到while迴圈的上方後,則不會出現該問題。即建立迭代器後不能再通過容器的add/remove方法來改變容器的資料,否則會導致迭代器的失效。

包括下面這種寫法,也是會導致同樣的異常

public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
        
    list.add("a");
    list.add("b");
    list.add("c");
    list.add("d");
    list.add("e");
    list.add("f");
    list.add("g");
    list.add("h");
        
    Iterator<String> it = list.iterator();
        
    while (it.hasNext()) {
        String str = it.next();
            
        if (str.equals("f")) {
            list.remove(str);
        }
    }
}
View Code

上面異常的本質是

remove操作裡涉及到的expectedModCount=modCount; 值為呼叫容器的iterator()方法返回iterator物件時,容器中的元素個數

在網上查到說這是集合迭代中的一種“快速失敗”機制,這種機制提供迭代過程中集合的安全性。

從原始碼裡可以看到增刪操作都會使modCount++,通過和expectedModCount的對比,迭代器可以快速的知道迭代過程中是否存在list.add()類似的操作,存在的話快速失敗!

而我們也知道,集合元素的刪除,不能用foreach 這又是為什麼?

foreach中的remove方法實際上使用list.remove一樣會報ConcurrentModificationException異常。因為foreach在jvm中還是會解析成Iterator來執行的,實際上和錯誤例子是一樣的效果。

那麼,我們再來看下為什麼用迭代器刪除時就可以安全的刪除,不會報錯呢?

在他的remove函式中可以看到下面的一句話,首先其實還是呼叫了ArrayList的remove函式

ArrayList.this.remove(lastRet)

但是在呼叫完該函式後,他又進行了如下操作

expectedModCount = modCount;

相當於將最新的版本號告訴了迭代器,所以迭代器在進行異常檢查的時候就不會報錯,因為他倆是相等的

四 迭代器刪除元素

public class ArrayListTest {
    public static void main(String args[]) throws Exception
    {
        List<String> strList = new ArrayList<String>();
        
        for (int i = 0; i < 10; i++)
        {
            strList.add("string" + i);
        }
        
        Iterator<String> iterator = strList.iterator();
        while (iterator.hasNext())
        {
            //iterator.next()  遊標指向了下一個元素
            if (iterator.next().equals("string3"))
            {
                iterator.remove();    //iterator.remove()移除的是最近一次iterator.next()所獲取的物件
            }
        }
        
        iterator = strList.iterator();
        while (iterator.hasNext())
        {
            System.out.println(iterator.next());
        }
    }
}

上述程式碼中,iterator.remove操作移除的物件時string3。如果將藍色while迴圈替換為如下的程式碼:

int index = 0;
        while (iterator.hasNext())
        {
            if (++index == 3)
            {
                iterator.remove();
            }
            System.out.println(iterator.next());

本程式碼的初衷是希望通過使用迭代器來刪除第三個元素即string2,並將未刪除的元素依次打印出來,殊不知刪除的卻是string1元素,即:iterator.remove()操作刪除的是上一次next元素獲取的物件。因此在這裡可以發現,如果要通過迭代器刪除一個元素,首先要通過next方法獲取該元素。

需要刪除元素,也可以把迭代器遍歷過程中,把需要刪除的元素放入新集合,遍歷完成後一次性刪除 removeAll()