Java 集合迭代器

問:簡單說說 Iterator 和 ListIterator 的區(qū)別?

答:區(qū)別主要如下酥夭。
(1)ListIterator 有 add() 方法枯芬,可以向 List 中添加對象,而 Iterator 不能采郎。

(2)ListIterator 和 Iterator 都有 hasNext() 和 next() 方法千所,可以實現(xiàn)順序向后遍歷,但是 ListIterator 有 hasPrevious() 和 previous() 方法蒜埋,可以實現(xiàn)逆向(順序向前)遍歷淫痰,Iterator 就不可以。

(3)ListIterator 可以定位當(dāng)前的索引位置整份,通過 nextIndex() 和 previousIndex() 可以實現(xiàn)待错,Iterator 沒有此功能。

(4)都可實現(xiàn)刪除對象烈评,但是 ListIterator 可以實現(xiàn)對象的修改火俄,通過 set() 方法可以實現(xiàn),Iterator 僅能遍歷讲冠,不能修改瓜客。

(5)ListIterator 是 Iterator 的子接口。

注意:容器類提供的迭代器都會在迭代中間進(jìn)行結(jié)構(gòu)性變化檢測竿开,如果容器發(fā)生了結(jié)構(gòu)性變化谱仪,就會拋出 ConcurrentModificationException,所以不能在迭代中間直接調(diào)用容器類提供的 add否彩、remove 方法疯攒,如需添加和刪除,應(yīng)調(diào)用迭代器的相關(guān)方法列荔。

問:為什么使用 for-each 時調(diào)用 List 的 remove 方法元素會拋出 ConcurrentModificationException 異常敬尺?

答:首先 Java 提供了一個 Iterable 接口返回一個迭代器,常用的 Collection<E>贴浙、List<E>砂吞、Set<E> 等都實現(xiàn)了這個接口,該接口的 iterator() 方法返回一個標(biāo)準(zhǔn)的 Iterator 實現(xiàn)悬而,實現(xiàn) Iterable 接口允許對象成為 for-each 語句的目標(biāo)來遍歷底層集合序列呜舒,因此使用 for-each 方式遍歷列表在編譯后實質(zhì)是迭代器的形式實現(xiàn)。之所以會出現(xiàn) ConcurrentModificationException 異常我們需要去看下最常見的 ArrayList 中 iterator() 方法的實現(xiàn)(別的集合 iterator 類似)笨奠,如下:

        private class Itr implements Iterator<E> {
            protected int limit = ArrayList.this.size; //集合列表的個數(shù)尺寸
            int cursor; //下一個元素的索引位置
            int lastRet = -1; //上一個元素的索引位置
            int expectedModCount = modCount;

            public boolean hasNext() {
                return cursor < limit;
            }

            @SuppressWarnings("unchecked")
            public E next() {
                //modCount用于記錄ArrayList集合的修改次數(shù)袭蝗,初始化為0唤殴,
                // 每當(dāng)集合被修改一次(結(jié)構(gòu)上面的修改,內(nèi)部update不算)到腥,
                // 如add朵逝、remove等方法,modCount + 1乡范,所以如果modCount不變配名,
                // 則表示集合內(nèi)容沒有被修改。
                if (modCount != expectedModCount) throw new ConcurrentModificationException();
                int i = cursor; //如果下一個元素的索引位置超過了集合長度拋出異常 
                if (i >= limit) throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException(); //調(diào)用一次cursor加一次 
                cursor = i + 1;
                //返回當(dāng)前一個元素
                return (E) elementData[lastRet = i];
            }

            public void remove() {
                //lastRet每次在remove成功后都需要在next()中重新賦值晋辆, 
                // 否則調(diào)用一次后再調(diào)用為-1異常渠脉,因此使用迭代器的remove方法 
                // 前必須先調(diào)用next()方法。 
                if (lastRet < 0) throw new IllegalStateException();
                if (modCount != expectedModCount) throw new ConcurrentModificationException();
                try {
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;
                    limit--;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
                   ......
        }

通過上面的源碼發(fā)現(xiàn)迭代操作中都有判斷 modCount != expectedModCount 的操作瓶佳,在 ArrayList 中 modCount 是當(dāng)前集合的版本號芋膘,每次修改(增、刪)集合都會加 1霸饲,expectedModCount 是當(dāng)前迭代器的版本號为朋,在迭代器實例化時初始化為 modCount,所以當(dāng)調(diào)用 ArrayList.add()ArrayList.remove() 時只是更新了 modCount 的狀態(tài)厚脉,而迭代器中的 expectedModCount 未修改习寸,因此才會導(dǎo)致再次調(diào)用 Iterator.next() 方法時拋出 ConcurrentModificationException 異常。而使用 Iterator.remove() 方法沒有問題是因為 Iterator 的 remove() 方法中有同步 expectedModCount 值傻工,所以當(dāng)下次再調(diào)用 next() 時檢查不會拋出異常霞溪。這其實是一種快速失敗機制,機制的規(guī)則就是當(dāng)多個線程對 Collection 進(jìn)行操作時若其中某一個線程通過 Iterator 遍歷集合時該集合的內(nèi)容被其他線程所改變精钮,則拋出 ConcurrentModificationException 異常威鹿。

因此在使用 Iterator 遍歷操作集合時應(yīng)該保證在遍歷集合的過程中不會對集合產(chǎn)生結(jié)構(gòu)上的修改剃斧,如果在遍歷過程中需要修改集合元素則一定要使用迭代器提供的修改方法而不是集合自身的修改方法轨香,此外 for-each 循環(huán)遍歷的實質(zhì)是迭代器,使用迭代器的 remove() 方法前必須先調(diào)用迭代器的 next() 方法且不允許調(diào)用一次 next() 方法后調(diào)用多次 remove() 方法幼东。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末臂容,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子根蟹,更是在濱河造成了極大的恐慌脓杉,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件简逮,死亡現(xiàn)場離奇詭異球散,居然都是意外死亡,警方通過查閱死者的電腦和手機散庶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門蕉堰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凌净,“玉大人,你說我怎么就攤上這事屋讶”埃” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵皿渗,是天一觀的道長斩芭。 經(jīng)常有香客問我,道長乐疆,這世上最難降的妖魔是什么划乖? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮挤土,結(jié)果婚禮上迁筛,老公的妹妹穿的比我還像新娘。我一直安慰自己耕挨,他們只是感情好细卧,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筒占,像睡著了一般贪庙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翰苫,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天止邮,我揣著相機與錄音,去河邊找鬼奏窑。 笑死导披,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的埃唯。 我是一名探鬼主播撩匕,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼墨叛!你這毒婦竟也來了止毕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤漠趁,失蹤者是張志新(化名)和其女友劉穎扁凛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闯传,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡谨朝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片字币。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡荚孵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纬朝,到底是詐尸還是另有隱情收叶,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布共苛,位于F島的核電站判没,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏隅茎。R本人自食惡果不足惜澄峰,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辟犀。 院中可真熱鬧俏竞,春花似錦、人聲如沸堂竟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽出嘹。三九已至席楚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間税稼,已是汗流浹背烦秩。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留郎仆,地道東北人只祠。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像扰肌,于是被迫代替她去往敵國和親抛寝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1.Java集合框架是什么狡耻?說出一些集合框架的優(yōu)點墩剖? 每種編程語言中都有集合,最初的Java版本包含幾種集合類:V...
    獨念白閱讀 770評論 0 2
  • Java 集合框架系列 Java 集合框架_開篇Java 集合框架_ListJava 集合框架_ArrayList...
    wo883721閱讀 830評論 0 3
  • Java源碼研究之容器(1) 如何看源碼 很多時候我們看源碼, 看完了以后經(jīng)常也沒啥收獲, 有些地方看得懂, 有些...
    駱駝騎士閱讀 994評論 0 22
  • 1.Java集合框架是什么夷狰?說出一些集合框架的優(yōu)點? 每種編程語言中都有集合郊霎,最初的Java版本包含幾種集合類:V...
    Oneisall_81a5閱讀 901評論 0 11
  • 親愛的自己: 今天聽了武志紅老師的心理課《輪回沼头,是為了療愈》,武老師所說的輪回,是心理學(xué)意義上的強迫性重復(fù)进倍。他主要...
    畢丹丹閱讀 204評論 0 1