java基礎之:迭代器詳解

我不是廖雪峰發表於2020-11-15

java基礎之:迭代器詳解?

一、為什麼要使用迭代器?

前景:
因為像list這種有序的集合裡邊,可以直接通過for迴圈的方式get(index)遍歷得到每一個元素, 而set這種沒有無序集合,則不能通過get(index)得到集合裡邊的元素,java就提供了迭代器,雖增強for迴圈也能對無序的集合進行遍歷,但其內部亦是採用迭代器實現。
優勢:
迭代器提供一種對容器物件中的各個元素進行訪問的方法,而又不需暴露該物件的內部細節。迭代器是一種模式,它可以使得對於序列型別的資料結構的遍歷行為與被遍歷的物件分離,即我們無需關心該序列的底層結構是什麼樣子的。只要拿到這個物件,使用迭代器就可以遍歷這個物件的內部.

    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    list.add("4");
    for (int i = 0; i < list.size(); i++) {
        list.get(i);//可以通過for迴圈對list進行遍歷
    }


    Set<String> set = new HashSet<>();
    set.add("1");
    set.add("2");
    set.add("3");
    set.add("4");
    for (int i = 0; i < set.size(); i++) {
        // 不能使用for迴圈對set進行遍歷
    }

二、如何使用迭代器?

控制檯列印:
在這裡插入圖片描述
需要遍歷的集合呼叫iterator()方法,返回一個迭代器物件,hashNext()方法判斷這個集合裡邊是否還有未遍歷完的元素,直到集合沒有元素可遍歷迴圈結束則結束

迭代器遍歷while迴圈寫法:

    Set<String> set = new HashSet<>();
    set.add("1");
    set.add("2");
    set.add("3");
    set.add("4");

    Iterator<String> iterator = set.iterator();
    
    while (iterator.hasNext()) {
        String next = iterator.next();
        System.out.println(next);
    }

迭代器遍歷for迴圈寫法:

    Set<String> set = new HashSet<>();
    set.add("1");
    set.add("2");
    set.add("3");
    set.add("4");

    for (Iterator<String> iterator = set.iterator(); iterator.hasNext(); ) {
        String next = iterator.next();
        System.out.println(next);
    }

控制檯列印:
在這裡插入圖片描述

三、List、Set迭代器實現原理及原始碼分析?

以List為例,因為List是介面,不可能有迭代器相關具體實現,所以檢視某具體實現類(以ArrayList為例)原始碼(多型),在AarrayList中發現iterator()方法返回了一個Itr物件,進而跟進Itr類。

 public Iterator<E> iterator() {
        return new Itr();
    }

Itr是一個內部類,Itr相關變數和實現迭代器相關細節的三個核心方法:

    private class Itr implements Iterator<E> {
		int cursor;       
		int lastRet = -1; 
		int expectedModCount = modCount;
        public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    hasNext();//此處省略方法具體實現,程式碼單獨貼在下面,以便於分析
    next();//此處省略方法具體實現,程式碼單獨貼在下面,以便於分析
    remove();//此處省略方法具體實現,程式碼單獨貼在下面,以便於分析
    }

一、hasNext()判斷是否有元素沒有被遍歷,其實現細節是判斷當前list的長度是否等於遊標

 public boolean hasNext() {
            return cursor != size;
        }

二、next()返回遊標當前位置的元素,並且遊標位置+1

     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()方法之後,該操作只能執行一次

        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();
            }
        }

原理圖如下:
在這裡插入圖片描述

四、補充:

for迴圈、增強for迴圈和迭代器的區別

1、迭代器是用於方便集合遍歷的,實現了Iterable介面的集合都可以使用迭代器來遍歷。使用迭代器遍歷元素時,除了獲取元素之外,只能做remove操作。

   for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
            iterator.remove();//只能做remove操作。
        }

2、增強for迴圈,內部使用的是迭代器,所以它的操作物件是陣列和可以使用迭代器的集合。遍歷時只能獲取元素,無法修改、刪除、增加。

    for (String s : list) {
        //無法修改、刪除、增加。
        }

3、如果需要對遍歷的物件做增刪修改的操作,使用普通的for迴圈來操作。

    for (int i = 0; i < list.size(); i++) {
            list.add(list.get(i));//增加對應元素
            list.remove(list.get(i));//刪除對應元素
            list.set(0, list.get(i));//修改對應的元素
        }

相關文章