迭代器

Enumeration<E>

在說Iterator之前就先說一下被代替的Enumeration<E>

Public interface enumerator<E>{

Public boolean hasMoreElements();   //是否還有元素

Public E nextElement();       //返回下一個元素

}

 

Enumeration 1.0的時候就推出了在當時出現的的集合也是用Hashtable Vector也是用它來進行遍歷的

jdk1.2的時候就推出了Iterator代替他

直到Jdk1.5Enumeration才加入了泛型,但是也沒能拯救他

 

Iterator(迭代器)Enumeration(枚舉類)區別

迭代器不僅可以讀取集合中的數據還可以移除數據,Enumeration只能遍歷

迭代器的名字短

迭代器支持fail-fast機制,Enumeration不支持

Enumeration本身是不支持同步的,但是在VoctorHashtable實現Enumeration,添加了同步

Iterator是支持fail-fast機制的:當多個線程對同一個集合的內容進行操作時,就可能會產生fail-fast事件。

 

迭代器Iterator(什麼樣的):

提供一種方法對一個容器對象中的元素進行訪問,而有不暴露該對象容器的內部細節

javaItertor是一個接口,只提供了基本的規則

Public interface Iterator<E>{

Boolean hasNext();//判斷是否有系一個元素

E next()//獲取下一個元素

Void remove;//移除元素

default void forEachRemaining(Consumer<? super E> action) {  

    Objects.requireNonNull(action);

    while (hasNext())

        action.accept(next());

}

}

 

Java還提供了一個接口Iterable(能夠...)

Public interface Iterable<T>{

Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) { //foreach循環

        Objects.requireNonNull(action);

        for (T t : this) {

            action.accept(t);

        }

    }

    default Spliterator<T> spliterator() {                //jdk1.8的新迭代器

        return Spliterators.spliteratorUnknownSize(iterator(), 0);

    }

}

Iterable接口實現後會返回一個迭代器iterator;常用的集合collection類都實現了這個接口.

Spliteratorsplitable iterator可分割迭代器)接口是Java爲了並行遍歷數據源中的元素而設計的迭代器,這個可以類比最早Java提供的順序遍歷迭代器Iterator,但一個是順序遍歷,一個是並行遍歷(等什麼時候成爲了dalao在研究這個都是些啥東西啊)

作用

返回一個標準的Iterable實現,實現Iterable接口了就可以通過foreach來遍歷.在編譯時會對foreach進行優化,轉換爲HashNext()next()的調

 

爲什麼要在弄兩個接口呢,直接實現Iterator不好嗎

主要是爲了迭代器之間不會相互干擾.

 

Iterator接口中的核心方法next()hasNext()remove(),都是依賴當前位置。如果這些集合直接實現Iterator接口,則勢必導致集合對象中包含當前迭代位置的數據(指針)。當集合在不同方法間進行傳遞的時候,由於當前迭代位置不可知,所以next()的結果也不可知。除非再爲Iterator接口添加一個reset()方法,用來重置當前迭代位置。 而當實現Iterable則不然,每次調用都返回一個從頭開始的迭代器,各個迭代器之間互不影響。

 

Iterator進行遍歷的時候不能對正在遍歷的容器進行改變大小的操作

看下Arraylist實現的Iterator

private class Itr implements Iterator<E> {

        int cursor;       // 表示下一個元素的索引位置

        int lastRet = -1; // 表示上一個元素的索引位置

        int expectedModCount = modCount;//預期被修改的次數

 

        public boolean hasNext() {

            return cursor != size; //當下一個索引的位置不等於元素個數的話 說明還有元素

        }

 

        @SuppressWarnings("unchecked")

        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];

        }

        final void checkForComodification() {

            if (modCount != expectedModCount)

                throw new ConcurrentModificationException();

        }

 

 

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

            }

        }

 

    }

fail-fast機制

集合的內部有一個modCount用來記錄集合被修改的次數 而迭代器自己也有一個expectedModCount記錄修改次數

 

根據最上面的成員屬性 可以知道modexpected剛開始是相同的 這時候如果遍歷的時候直接用集合的方法修改集合那麼modexpected就會不同(checkForComodification) 就會拋出concurrxxxxx的異常  

 

而如果是用迭代器自己的remove方法改動 那麼兩個將同時改變就不會報錯

 

同時有個有趣的現象,ArrayList不用迭代器的方法刪除倒數第二個元素也不是觸發fail-fast機制

注意看那個hasNext方法

在刪除倒數第二個元素的場景下:當倒數第二個元素時迭代完成,開始迭代最後一個元素時,此時cursor4size由於在迭代過程倒數第二個元素移除了,所以-1, 此時cursorsize相等,遍歷結束,不會再進入下一個迭代,因此不會觸發checkForComodification方法的fail-fast機制。

 

ListIterator

感覺Iterator不夠強大,只能移除啊,好多功能沒有

Java又出來一個新的接口,ListIterator接口,繼承Iterator接口

雙向遍歷 可以在遍歷期間修改集合,同時可以獲取迭代器遊標的在集合中的位置

遺憾的是,只有在list其子類才能使用

兩個獲取方式

List.listIterator()

List.listIterator(int location) 可以指定遊標的位置

 

 

AbstractListlist的直接子類,就實現了ListIterator()方法,並且有兩個內部迭代器實現類

 

SimpleListIteratorFullListIterator

SimpleListIterator 的主要操作最後都交給子類來實現,List 的子類 ArrayList, LinkedList, Vector 由於底層實現原理不同(數組,雙向鏈表),具體操作類實現有所不同。

 

FullListIterator 繼承了 SimpleListIterator, SimpleListIterator 實現了 Iterator 接口:

Listlterator()返回的fulllistlterator();

 

ListIterator

Public ListIterator<E> listIterator(int location){

Return new fulllistiterator(location);

}

 

SimpleListIterator的源碼

private class SimpleListIterator implements Iterator<E> {

    //遊標的位置,初始爲 -1

    int pos = -1;

    //用來判斷是否 fail-fast 的變量

    int expectedModCount;

    //記錄上次迭代的位置

    int lastPosition = -1;

 

    SimpleListIterator() {

        expectedModCount = modCount;

    }

 

    //當遊標沒有跑到最後一個元素後面時 hasNext 返回 true

    public boolean hasNext() {

        return pos + 1 < size();

    }

 

    //獲取下一個元素

    public E next() {

        if (expectedModCount == modCount) {

            try {

                //獲取遊標後面的元素,具體子類有具體實現

                E result = get(pos + 1);

                //更新

                lastPosition = ++pos;

                return result;

            } catch (IndexOutOfBoundsException e) {

                throw new NoSuchElementException();

            }

        }

        //當迭代時修改元素,就會報這個錯

        throw new ConcurrentModificationException();

    }

 

    //刪除上次迭代操作的元素

    public void remove() {

        //還沒進行迭代操作就會報這個錯

        if (this.lastPosition == -1) {

            throw new IllegalStateException();

        }

 

        if (expectedModCount != modCount) {

            throw new ConcurrentModificationException();

        }

 

        try {

            //調用子類實現的刪除操作

            AbstractList.this.remove(lastPosition);

        } catch (IndexOutOfBoundsException e) {

            throw new ConcurrentModificationException();

        }

 

        expectedModCount = modCount;

        if (pos == lastPosition) {

            pos--;

        }

        //每次刪除後都會還原爲 -1,也就是說我們迭代一次後只能 remove 一次,再 remove 就會報錯

        lastPosition = -1;

    }

}

//FullListIterator源碼

private final class FullListIterator extends SimpleListIterator implements ListIterator<E> {

    //根據 start 指定遊標位置

    FullListIterator(int start) {

        if (start >= 0 && start <= size()) {

            pos = start - 1;

        } else {

            throw new IndexOutOfBoundsException();

        }

    }

 

    //在遊標前面添加元素

    public void add(E object) {

        if (expectedModCount == modCount) {

            try {

                //調用子類的添加操作,ArrayList, LinkedList,Vector 的添加操作實現有所不同

                AbstractList.this.add(pos + 1, object);

            } catch (IndexOutOfBoundsException e) {

                throw new NoSuchElementException();

            }

            //遊標後移一位

            pos++;

            //!注意! 添加後 上次迭代位置又變回 -1 了,說明 add 後調用 remove, set 會有問題!

            lastPosition = -1;

            if (modCount != expectedModCount) {

                expectedModCount = modCount;

            }

        } else {

            throw new ConcurrentModificationException();

        }

    }

 

    //當遊標不在初始位置(-1)時返回true

    public boolean hasPrevious() {

        return pos >= 0;

    }

 

    //遊標後面的元素索引,就是遊標 +1

    public int nextIndex() {

        return pos + 1;

    }

 

    //遊標前面一個元素

    public E previous() {

        if (expectedModCount == modCount) {

            try {

                E result = get(pos);

                lastPosition = pos;

                pos--;

                return result;

            } catch (IndexOutOfBoundsException e) {

                throw new NoSuchElementException();

            }

        }

        throw new ConcurrentModificationException();

    }

 

    //遊標前面元素的索引

    public int previousIndex() {

        return pos;

    }

 

    //更新之前迭代的元素爲 object

    public void set(E object) {

        if (expectedModCount == modCount) {

            try {

                //調用子類的set

                AbstractList.this.set(lastPosition, object);

            } catch (IndexOutOfBoundsException e) {

                throw new IllegalStateException();

            }

        } else {

            throw new ConcurrentModificationException();

        }

    }

}

 

 


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