摘要:總結(jié)循環(huán)性能在三者的對(duì)比中總體落于下風(fēng),而且開(kāi)銷(xiāo)遞增幅度較大。的性能在數(shù)組以及鏈表的表現(xiàn)都是最好的,應(yīng)該是的設(shè)計(jì)者優(yōu)化過(guò)了。
????本文首發(fā)于cartoon的博客
????轉(zhuǎn)載請(qǐng)注明出處:https://cartoonyu.github.io/cartoon-blog/post/java/java%E9%81%8D%E5%8E%86%E6%9C%BA%E5%88%B6%E7%9A%84%E6%80%A7%E8%83%BD%E6%AF%94%E8%BE%83/
????近段時(shí)間在寫(xiě)leetcode的Lemonade Change時(shí)候,發(fā)現(xiàn)了for循環(huán)與forEach循環(huán)的耗時(shí)是不一致的,在提交記錄上面差了一倍......
????平常開(kāi)發(fā)絕大部分業(yè)務(wù)邏輯的實(shí)現(xiàn)都需要遍歷機(jī)制的幫忙,雖說(shuō)也有注意到各數(shù)據(jù)結(jié)構(gòu)操作的性能比較,但是忽視了遍歷機(jī)制性能的差異。原本前兩天就開(kāi)始動(dòng)手寫(xiě),拖延癥......
????現(xiàn)階段我所知道JAVA遍歷機(jī)制有三種
for循環(huán)
forEach循環(huán)
Iterator循環(huán)
????JAVA數(shù)據(jù)結(jié)構(gòu)千千萬(wàn),但是大部分都是對(duì)基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)的封裝,比較HashMap依賴于Node數(shù)組,LinkedList底層是鏈表,ArrayList對(duì)數(shù)組的再封裝......扯遠(yuǎn)了
????總結(jié)來(lái)說(shuō),JAVA的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),我覺(jué)得有兩種
數(shù)組
鏈表
????如果是加上Hash(Hash的操作與數(shù)組以及鏈表不太一致),就是三種
????因?yàn)槠匠i_(kāi)發(fā)大部分都優(yōu)先選擇包裝后的數(shù)據(jù)結(jié)構(gòu),所以下面我會(huì)使用
ArrayList(包裝后的數(shù)組)
LinkedList(包裝后的鏈表)
HashSet(包裝后的Hash類(lèi)型數(shù)組)
????這三種數(shù)據(jù)結(jié)構(gòu)在遍歷機(jī)制不同的時(shí)候時(shí)間的差異
????可能有人對(duì)我為什么不對(duì)比HashMap呢,因?yàn)镴AVA設(shè)計(jì)中,是先實(shí)現(xiàn)了Map,再實(shí)現(xiàn)Set。如果你有閱讀過(guò)源碼就會(huì)發(fā)現(xiàn):每個(gè)Set子類(lèi)的實(shí)現(xiàn)中,都有一個(gè)序列化后的Map對(duì)應(yīng)屬性實(shí)現(xiàn),而因?yàn)镠ash的查找時(shí)間復(fù)雜度為O(1),得出key后查找value的時(shí)間大致是一致的,所以我不對(duì)比HashMap。
題外話
????我在閱讀《瘋狂JAVA》讀到:JAVA的設(shè)計(jì)者將Map的內(nèi)部entry數(shù)組中的value設(shè)為null進(jìn)而實(shí)現(xiàn)了Set。因?yàn)槲沂且栽创a以及官方文檔為準(zhǔn),具體我不清楚正確與否,但是因?yàn)镠ash中的key互不相同,Set中元素也互不相同,所以我認(rèn)為這個(gè)觀點(diǎn)是正確的。
????為了測(cè)試的公平性,我會(huì)采取以下的限定
每種數(shù)據(jù)結(jié)構(gòu)的大小都設(shè)置三種量級(jí)
10
100
1000
元素都采用隨機(jī)數(shù)生成
遍歷進(jìn)行操作都為輸出當(dāng)前元素的值
????注:時(shí)間開(kāi)銷(xiāo)受本地環(huán)境的影響,可能測(cè)量值會(huì)出現(xiàn)變化,但是總體上比例是正確的
ArrayList的比較
代碼
public class TextArray { private static Random random; private static Listlist1; private static List list2; private static List list3; public static void execute(){ random=new Random(); initArray(); testForWith10Object(); testForEachWith10Object(); testIteratorWith10Object(); testForWith100Object(); testForEachWith100Object(); testIteratorWith100Object(); testForWith1000Object(); testForEachWith1000Object(); testIteratorWith1000Object(); } private static void testForWith10Object(){ printFor(list1); } private static void testForWith100Object(){ printFor(list2); } private static void testForWith1000Object(){ printFor(list3); } private static void testForEachWith10Object(){ printForeach(list1); } private static void testForEachWith100Object(){ printForeach(list2); } private static void testForEachWith1000Object(){ printForeach(list3); } private static void testIteratorWith10Object() { printIterator(list1); } private static void testIteratorWith100Object() { printIterator(list2); } private static void testIteratorWith1000Object() { printIterator(list3); } private static void printFor(List list){ System.out.println(); System.out.print("data:"); long start=System.currentTimeMillis(); for(int i=0,length=list.size();i list){ System.out.println(); System.out.print("data:"); long start=System.currentTimeMillis(); for(int temp:list){ System.out.print(temp+" "); } System.out.println(); long end=System.currentTimeMillis(); System.out.println("foreach for "+list.size()+":"+(end-start)+"ms"); } private static void printIterator(List list){ System.out.println(); System.out.print("data:"); Iterator it=list.iterator(); long start=System.currentTimeMillis(); while(it.hasNext()){ System.out.print(it.next()+" "); } System.out.println(); long end=System.currentTimeMillis(); System.out.println("iterator for "+list.size()+":"+(end-start)+"ms"); } private static void initArray(){ list1=new ArrayList<>(); list2=new ArrayList<>(); list3=new ArrayList<>(); for(int i=0;i<10;i++){ list1.add(random.nextInt()); } for(int i=0;i<100;i++){ list2.add(random.nextInt()); } for(int i=0;i<1000;i++){ list3.add(random.nextInt()); } } }
輸出(忽略對(duì)元素的輸出)
for for 10:1ms foreach for 10:0ms iterator for 10:2ms for for 100:5ms foreach for 100:4ms iterator for 100:12ms for for 1000:33ms foreach for 1000:7ms iterator for 1000:16ms
10 | 100 | 1000 | |
---|---|---|---|
for | 1ms | 5ms | 33ms |
forEach | 0ms | 4ms | 7ms |
Iterator | 2ms | 12ms | 16ms |
結(jié)論
????for的性能最不穩(wěn)定,foreach次之,Iterator最好
使用建議
在數(shù)據(jù)量不明確的情況下(可能1w,10w或其他),建議使用Iterator進(jìn)行遍歷
在數(shù)據(jù)量明確且量級(jí)小的時(shí)候,優(yōu)先使用foreach
需要使用索引時(shí),使用遞增變量的開(kāi)銷(xiāo)比f(wàn)or的要小
LinkedList的比較
代碼
public class TextLinkedList { private static Random random; private static Listlist1; private static List list2; private static List list3; public static void execute(){ random=new Random(); initList(); testForWith10Object(); testForEachWith10Object(); testIteratorWith10Object(); testForWith100Object(); testForEachWith100Object(); testIteratorWith100Object(); testForWith1000Object(); testForEachWith1000Object(); testIteratorWith1000Object(); } private static void testForWith10Object() { printFor(list1); } private static void testForEachWith10Object() { printForeach(list1); } private static void testIteratorWith10Object() { printIterator(list1); } private static void testForWith100Object() { printFor(list2); } private static void testForEachWith100Object() { printForeach(list2); } private static void testIteratorWith100Object() { printIterator(list2); } private static void testForWith1000Object() { printFor(list3); } private static void testForEachWith1000Object() { printForeach(list3); } private static void testIteratorWith1000Object() { printIterator(list3); } private static void printFor(List list){ System.out.println(); System.out.print("data:"); long start=System.currentTimeMillis(); for(int i=0,size=list.size();i list){ System.out.println(); System.out.print("data:"); long start=System.currentTimeMillis(); for(int temp:list){ System.out.print(temp+" "); } System.out.println(); long end=System.currentTimeMillis(); System.out.println("foreach for "+list.size()+":"+(end-start)+"ms"); } private static void printIterator(List list){ System.out.println(); System.out.print("data:"); Iterator it=list.iterator(); long start=System.currentTimeMillis(); while(it.hasNext()){ System.out.print(it.next()+" "); } System.out.println(); long end=System.currentTimeMillis(); System.out.println("iterator for "+list.size()+":"+(end-start)+"ms"); } private static void initList() { list1=new LinkedList<>(); list2=new LinkedList<>(); list3=new LinkedList<>(); for(int i=0;i<10;i++){ list1.add(random.nextInt()); } for(int i=0;i<100;i++){ list2.add(random.nextInt()); } for(int i=0;i<1000;i++){ list3.add(random.nextInt()); } } }
輸出(忽略對(duì)元素的輸出)
for for 10:0ms foreach for 10:1ms iterator for 10:0ms for for 100:1ms foreach for 100:0ms iterator for 100:3ms for for 1000:23ms foreach for 1000:25ms iterator for 1000:4ms
10 | 100 | 1000 | |
---|---|---|---|
for | 0ms | 1ms | 23ms |
forEach | 1ms | 0ms | 25ms |
Iterator | 0ms | 3ms | 4ms |
結(jié)論
????foreach的性能最不穩(wěn)定,for次之,Iterator最好
使用建議
盡量使用Iterator進(jìn)行遍歷
需要使用索引時(shí),使用遞增變量的開(kāi)銷(xiāo)比f(wàn)or的要小
HashSet的比較????注:因Hash遍歷算法與其他類(lèi)型不一致,所以取消了for循環(huán)的比較
代碼
public class TextHash { private static Random random; private static Setset1; private static Set set2; private static Set set3; public static void execute(){ random=new Random(); initHash(); testIteratorWith10Object(); testForEachWith10Object(); testIteratorWith100Object(); testForEachWith100Object(); testIteratorWith1000Object(); testForEachWith1000Object(); } private static void testIteratorWith10Object() { printIterator(set1); } private static void testForEachWith10Object() { printForeach(set1); } private static void testIteratorWith100Object() { printIterator(set2); } private static void testForEachWith100Object() { printForeach(set2); } private static void testIteratorWith1000Object() { printIterator(set3); } private static void testForEachWith1000Object() { printForeach(set3); } private static void initHash() { set1=new HashSet<>(); set2=new HashSet<>(); set3=new HashSet<>(); for(int i=0;i<10;i++){ set1.add(random.nextInt()); } for(int i=0;i<100;i++){ set2.add(random.nextInt()); } for(int i=0;i<1000;i++){ set3.add(random.nextInt()); } } private static void printIterator(Set data){ System.out.println(); System.out.print("data:"); long start=System.currentTimeMillis(); Iterator it=data.iterator(); while (it.hasNext()){ System.out.print(it.next()+" "); } System.out.println(); long end=System.currentTimeMillis(); System.out.println("iterator for "+data.size()+":"+(end-start)+"ms"); } private static void printForeach(Set data){ System.out.println(); System.out.print("data:"); long start=System.currentTimeMillis(); for(int temp:data){ System.out.print(temp+" "); } System.out.println(); long end=System.currentTimeMillis(); System.out.println("foreach for "+data.size()+":"+(end-start)+"ms"); } }
輸出(忽略對(duì)元素的輸出)
iterator for 10:0ms foreach for 10:0ms iterator for 100:6ms foreach for 100:0ms iterator for 1000:30ms foreach for 1000:9ms
10 | 100 | 1000 | |
---|---|---|---|
foreach | 0ms | 0ms | 9ms |
Iterator | 0ms | 6ms | 30ms |
結(jié)論
????foreach性能遙遙領(lǐng)先于Iterator
使用建議
????以后就選foreach了,性能好,寫(xiě)起來(lái)也方便。
總結(jié)for循環(huán)性能在三者的對(duì)比中總體落于下風(fēng),而且開(kāi)銷(xiāo)遞增幅度較大。以后即使在需要使用索引時(shí)我寧愿使用遞增變量也不會(huì)使用for了。
Iterator的性能在數(shù)組以及鏈表的表現(xiàn)都是最好的,應(yīng)該是JAVA的設(shè)計(jì)者優(yōu)化過(guò)了。在響應(yīng)時(shí)間敏感的情況下(例如web響應(yīng)),優(yōu)先考慮。
foreach的性能屬于兩者之間,寫(xiě)法簡(jiǎn)單,時(shí)間不敏感的情況下我會(huì)盡量選用。
????以上就是我對(duì)常見(jiàn)數(shù)據(jù)結(jié)構(gòu)遍歷機(jī)制的一點(diǎn)比較,雖然只是很初步,但是從中我也學(xué)到了很多東西,希望你們也有所收獲。
????如果你喜歡本文章,可以收藏閱讀,如果你對(duì)我的其他文章感興趣,歡迎到我的博客查看。
列表項(xiàng)目
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74894.html
摘要:體現(xiàn)的就是適配器模式。數(shù)組對(duì)象集合世界中的機(jī)制機(jī)制集合世界中比較常見(jiàn)的錯(cuò)誤檢測(cè)機(jī)制,防止在對(duì)集合進(jìn)行遍歷過(guò)程當(dāng)中,出現(xiàn)意料之外的修改,會(huì)通過(guò)異常暴力的反應(yīng)出來(lái)。而在增強(qiáng)循環(huán)中,集合遍歷是通過(guò)進(jìn)行的。 前言 學(xué)習(xí)情況記錄 時(shí)間:week 2 SMART子目標(biāo) :Java 容器 記錄在學(xué)習(xí)Java容器 知識(shí)點(diǎn)中,關(guān)于List的重點(diǎn)知識(shí)點(diǎn)。 知識(shí)點(diǎn)概覽: 容器中的設(shè)計(jì)模式 從Array...
摘要:操作系統(tǒng)是能夠獲取到事件操作完成的事件,基于回調(diào)函數(shù)機(jī)制和操作系統(tǒng)的操作控制實(shí)現(xiàn)事件檢測(cè)機(jī)制。 前面的文章NIO基礎(chǔ)知識(shí)介紹了Java NIO的一些基本的類(lèi)及功能說(shuō)明,Java NIO是用來(lái)替換java 傳統(tǒng)IO的,NIO的一些新的特性在網(wǎng)絡(luò)交互方面會(huì)更加的明顯。 Java 傳統(tǒng)IO的弊端 ????基于JVM來(lái)實(shí)現(xiàn)每個(gè)通道的輪詢檢查通道狀態(tài)的方法是可行的,但仍然是有問(wèn)題的,檢查每個(gè)通道...
摘要:是現(xiàn)在廣泛流行的代從開(kāi)始學(xué)習(xí)系列之向提交代碼掘金讀完本文大概需要分鐘。為了進(jìn)行高效的垃圾回收,虛擬機(jī)把堆內(nèi)存劃分成新生代老年代和永久代中無(wú)永久代,使用實(shí)現(xiàn)三塊區(qū)域。 React Native 開(kāi)源項(xiàng)目 - 仿美團(tuán)客戶端 (Android、iOS 雙適配) - Android - 掘金推薦 React Native 學(xué)習(xí)好項(xiàng)目,仿照美團(tuán)客戶端... 極簡(jiǎn) GitHub 上手教程 - 工具...
閱讀 2003·2021-11-19 09:40
閱讀 1963·2021-09-28 09:36
閱讀 2296·2021-09-22 10:02
閱讀 2738·2019-08-30 14:00
閱讀 1967·2019-08-29 15:31
閱讀 2908·2019-08-29 15:11
閱讀 2916·2019-08-29 13:04
閱讀 1090·2019-08-27 10:55