Java中集合類遍歷性能

集合元素在內(nèi)存如何存放

數(shù)據(jù)元素在內(nèi)存中乳讥,主要有2種存儲方式:
1颜及、順序存儲吐咳,Random Access(或直接存儲会宪,Direct Access):
這種方式肖卧,相鄰的數(shù)據(jù)元素存放于相鄰的內(nèi)存地址中,整塊內(nèi)存地址是連續(xù)的掸鹅∪剩可以根據(jù)元素的位置直接計算出內(nèi)存地址拦赠,直接進(jìn)行讀取。讀取一個特定位置元素的平均時間復(fù)雜度為O(1)葵姥。這種數(shù)據(jù)結(jié)構(gòu)插入和刪除時比較麻煩荷鼠,查詢比較方便。正常來說榔幸,只有基于數(shù)組實現(xiàn)的集合允乐,才有這種特性。Java中以ArrayList為代表削咆;

2牍疏、鏈?zhǔn)酱鎯Γ琒equential Access:
這種方式是將數(shù)據(jù)元素放在獨立的空間中拨齐,在內(nèi)存中每個元素的內(nèi)存地址都不要求是相鄰的鳞陨。所以每個元素中需要保存下一個元素的索引,即下一個元素的內(nèi)存地址瞻惋。所以這種數(shù)據(jù)結(jié)構(gòu)插入和刪除比較方便厦滤,但是查找很麻煩,要從第一個開始遍歷歼狼,因為它無法根據(jù)元素的位置直接計算出內(nèi)存地址掏导,只能按順序讀取元素。讀取一個特定位置元素的平均時間復(fù)雜度為O(n)羽峰。主要以鏈表為代表碘菜。Java中以LinkedList為代表;

Java集合遍歷方式

1)傳統(tǒng)的for循環(huán)遍歷基于計數(shù)器
遍歷者自己在集合外部維護(hù)一個計數(shù)器限寞,然后依次讀取每一個位置的元素忍啸,直到讀取到最后一個元素后。主要就是需要按元素的位置來讀取元素履植,適合于遍歷順序存儲的數(shù)據(jù)結(jié)構(gòu)计雌;

for (int i = 0; i < list.size(); i++) {
    list.get(i); 
}

2)迭代器遍歷Iterator
Iterator本來是OO的一個設(shè)計模式,主要目的就是屏蔽不同數(shù)據(jù)集合的特點玫霎,統(tǒng)一遍歷集合的接口凿滤。從結(jié)構(gòu)上可以看出,迭代器模式在客戶與容器之間加入了迭代器角色庶近。迭代器角色的加入翁脆,就可以很好的避免容器內(nèi)部細(xì)節(jié)的暴露,而且也使得設(shè)計符號“單一職責(zé)原則”鼻种;
每一個具體實現(xiàn)的數(shù)據(jù)集合反番,一般都需要提供相應(yīng)的Iterator。相比于傳統(tǒng)for循環(huán),Iterator取代了顯式的遍歷計數(shù)器罢缸。所以基于順序存儲集合的Iterator可以直接按位置訪問數(shù)據(jù)篙贸。而基于鏈?zhǔn)酱鎯系腎terator,正常的實現(xiàn)都是需要保存當(dāng)前遍歷的位置枫疆,然后根據(jù)當(dāng)前位置來向前或者向后移動指針

迭代器模式的寫法如下:

Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    iterator.next();
}

3)foreach循環(huán)遍歷
foreach內(nèi)部也是采用了Iterator的方式實現(xiàn)爵川,根據(jù)反編譯的字節(jié)碼可以發(fā)現(xiàn),foreach內(nèi)部也是采用了Iterator的方式實現(xiàn)息楔,只不過Java編譯器幫我們生成了這些代碼
優(yōu)點:代碼簡潔寝贡,不易出錯
缺點:只能做簡單的遍歷,不能在遍歷過程中操作(刪除值依、替換)數(shù)據(jù)集合

for (ElementType element : list) {
}

遍歷性能

1)傳統(tǒng)的for循環(huán)遍歷基于計數(shù)器
ArrayList按位置讀取的代碼:直接按元素位置讀取

public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}
E elementData(int index) {
    return (E) elementData[index];
}

LinkedList按位置讀取的代碼:如果index小于容量的一半兔甘,則從頭開始查找,否則從尾部開始查找

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

Node<E> node(int index) {
    // assert isElementIndex(index);
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

2)迭代器遍歷Iterator
對于RandomAccess類型的集合來說鳞滨,如ArrayList迭代器遍歷反而因為一些額外的操作洞焙,還會增加額外的運(yùn)行時間;
對于Sequential Access的集合來說拯啦,因為Iterator內(nèi)部維護(hù)了當(dāng)前遍歷的位置澡匪,所以每次遍歷,讀取下一個位置并不需要從集合的第一個元素開始查找褒链,只要把指針向后移一位就行了唁情,這樣一來遍歷整個集合的時間復(fù)雜度就降低為O(n);以LinkedList的迭代器為例甫匹,其內(nèi)部實現(xiàn)甸鸟,就是維護(hù)當(dāng)前遍歷的位置,然后操作指針移動就可以了

3)foreach循環(huán)遍歷
分析Java字節(jié)碼可知兵迅,foreach內(nèi)部實現(xiàn)原理是通過Iterator實現(xiàn)的抢韭,只不過這個Iterator是Java編譯器幫我們生成的,所以我們不需要再手動去編寫恍箭。但是因為每次都要做類型轉(zhuǎn)換檢查刻恭,所以花費(fèi)的時間比Iterator略長,而時間復(fù)雜度和Iterator是一樣

使用場景

1)傳統(tǒng)的for循環(huán)遍歷基于計數(shù)器
適用于遍歷順序存儲集合扯夭,讀取性能比較高如ArrayList鳍贾;不適用于遍歷鏈?zhǔn)酱鎯Φ募先鏛inkedList,時間復(fù)雜度太大交洗;

2)迭代器遍歷Iterator
對于順序存儲的數(shù)據(jù)結(jié)構(gòu)骑科,如果不是太在意時間,推薦選擇此方式构拳,畢竟代碼更加簡潔咆爽,也防止了Off-By-One的問題梁棠。
鏈?zhǔn)酱鎯Γ和扑]此種遍歷方式,平均時間復(fù)雜度降為O(n)

3)foreach循環(huán)遍歷
foreach讓代碼更加簡潔伍掀,缺點就是遍歷過程中不能操作數(shù)據(jù)集合(刪除等)掰茶,所以有些場合不使用暇藏。而且它本身就是基于Iterator實現(xiàn)的蜜笤,但是由于類型轉(zhuǎn)換的問題,所以會比直接使用Iterator慢一點盐碱,性能上相差不大

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末把兔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瓮顽,更是在濱河造成了極大的恐慌县好,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暖混,死亡現(xiàn)場離奇詭異缕贡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拣播,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門晾咪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贮配,你說我怎么就攤上這事谍倦。” “怎么了泪勒?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵昼蛀,是天一觀的道長。 經(jīng)常有香客問我圆存,道長叼旋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任沦辙,我火速辦了婚禮送淆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怕轿。我一直安慰自己偷崩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布撞羽。 她就那樣靜靜地躺著阐斜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诀紊。 梳的紋絲不亂的頭發(fā)上谒出,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼笤喳。 笑死为居,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杀狡。 我是一名探鬼主播蒙畴,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼呜象!你這毒婦竟也來了膳凝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤恭陡,失蹤者是張志新(化名)和其女友劉穎蹬音,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體休玩,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡著淆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拴疤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片永部。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖遥赚,靈堂內(nèi)的尸體忽然破棺而出扬舒,到底是詐尸還是另有隱情,我是刑警寧澤凫佛,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布讲坎,位于F島的核電站,受9級特大地震影響愧薛,放射性物質(zhì)發(fā)生泄漏晨炕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一毫炉、第九天 我趴在偏房一處隱蔽的房頂上張望瓮栗。 院中可真熱鬧,春花似錦瞄勾、人聲如沸费奸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽愿阐。三九已至,卻和暖如春趾疚,著一層夾襖步出監(jiān)牢的瞬間缨历,已是汗流浹背以蕴。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留辛孵,地道東北人丛肮。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像魄缚,于是被迫代替她去往敵國和親宝与。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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

  • 概述 Java語言中鲜滩,提供了一套數(shù)據(jù)集合框架伴鳖,其中定義了一些諸如List节值、Set等抽象數(shù)據(jù)類型徙硅,每個抽象數(shù)據(jù)類型的...
    一天一夜00閱讀 669評論 0 2
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,898評論 2 89
  • 今日任務(wù) 1、List接口介紹(掌握常用List特有方法)2搞疗、練習(xí)3嗓蘑、ArrayList介紹(必須清楚集合的特征、...
    Villain丶Cc閱讀 1,028評論 1 1
  • 1. 老公說要和我離婚匿乃。我正在洗碗桩皿,沒把手上的洗潔精泡沫抹干凈,我跟著他跑出來顫抖的問道幢炸,你說什么泄隔,要和我離婚? ...
    親哥拜閱讀 818評論 9 11
  • 元旦后第二天早上宛徊,友人從微信上發(fā)來照片佛嬉。杭州山居,空林黃葉闸天,正是我們前年居游的地方暖呕,水樂洞。那時金簇桂枝苞氮,天香縹緲...
    歲華錄閱讀 295評論 0 0