摘要:總結循環性能在三者的對比中總體落于下風,而且開銷遞增幅度較大。的性能在數組以及鏈表的表現都是最好的,應該是的設計者優化過了。
????本文首發于cartoon的博客
????轉載請注明出處: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/
????近段時間在寫leetcode的Lemonade Change時候,發現了for循環與forEach循環的耗時是不一致的,在提交記錄上面差了一倍......
????平常開發絕大部分業務邏輯的實現都需要遍歷機制的幫忙,雖說也有注意到各數據結構操作的性能比較,但是忽視了遍歷機制性能的差異。原本前兩天就開始動手寫,拖延癥......
????現階段我所知道JAVA遍歷機制有三種
for循環
forEach循環
Iterator循環
????JAVA數據結構千千萬,但是大部分都是對基礎數據結構的封裝,比較HashMap依賴于Node數組,LinkedList底層是鏈表,ArrayList對數組的再封裝......扯遠了
????總結來說,JAVA的基礎數據結構,我覺得有兩種
數組
鏈表
????如果是加上Hash(Hash的操作與數組以及鏈表不太一致),就是三種
????因為平常開發大部分都優先選擇包裝后的數據結構,所以下面我會使用
ArrayList(包裝后的數組)
LinkedList(包裝后的鏈表)
HashSet(包裝后的Hash類型數組)
????這三種數據結構在遍歷機制不同的時候時間的差異
????可能有人對我為什么不對比HashMap呢,因為JAVA設計中,是先實現了Map,再實現Set。如果你有閱讀過源碼就會發現:每個Set子類的實現中,都有一個序列化后的Map對應屬性實現,而因為Hash的查找時間復雜度為O(1),得出key后查找value的時間大致是一致的,所以我不對比HashMap。
題外話
????我在閱讀《瘋狂JAVA》讀到:JAVA的設計者將Map的內部entry數組中的value設為null進而實現了Set。因為我是以源碼以及官方文檔為準,具體我不清楚正確與否,但是因為Hash中的key互不相同,Set中元素也互不相同,所以我認為這個觀點是正確的。
????為了測試的公平性,我會采取以下的限定
每種數據結構的大小都設置三種量級
10
100
1000
元素都采用隨機數生成
遍歷進行操作都為輸出當前元素的值
????注:時間開銷受本地環境的影響,可能測量值會出現變化,但是總體上比例是正確的
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()); } } }
輸出(忽略對元素的輸出)
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 |
結論
????for的性能最不穩定,foreach次之,Iterator最好
使用建議
在數據量不明確的情況下(可能1w,10w或其他),建議使用Iterator進行遍歷
在數據量明確且量級小的時候,優先使用foreach
需要使用索引時,使用遞增變量的開銷比for的要小
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()); } } }
輸出(忽略對元素的輸出)
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 |
結論
????foreach的性能最不穩定,for次之,Iterator最好
使用建議
盡量使用Iterator進行遍歷
需要使用索引時,使用遞增變量的開銷比for的要小
HashSet的比較????注:因Hash遍歷算法與其他類型不一致,所以取消了for循環的比較
代碼
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"); } }
輸出(忽略對元素的輸出)
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 |
結論
????foreach性能遙遙領先于Iterator
使用建議
????以后就選foreach了,性能好,寫起來也方便。
總結for循環性能在三者的對比中總體落于下風,而且開銷遞增幅度較大。以后即使在需要使用索引時我寧愿使用遞增變量也不會使用for了。
Iterator的性能在數組以及鏈表的表現都是最好的,應該是JAVA的設計者優化過了。在響應時間敏感的情況下(例如web響應),優先考慮。
foreach的性能屬于兩者之間,寫法簡單,時間不敏感的情況下我會盡量選用。
????以上就是我對常見數據結構遍歷機制的一點比較,雖然只是很初步,但是從中我也學到了很多東西,希望你們也有所收獲。
????如果你喜歡本文章,可以收藏閱讀,如果你對我的其他文章感興趣,歡迎到我的博客查看。
列表項目
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74894.html
摘要:體現的就是適配器模式。數組對象集合世界中的機制機制集合世界中比較常見的錯誤檢測機制,防止在對集合進行遍歷過程當中,出現意料之外的修改,會通過異常暴力的反應出來。而在增強循環中,集合遍歷是通過進行的。 前言 學習情況記錄 時間:week 2 SMART子目標 :Java 容器 記錄在學習Java容器 知識點中,關于List的重點知識點。 知識點概覽: 容器中的設計模式 從Array...
摘要:操作系統是能夠獲取到事件操作完成的事件,基于回調函數機制和操作系統的操作控制實現事件檢測機制。 前面的文章NIO基礎知識介紹了Java NIO的一些基本的類及功能說明,Java NIO是用來替換java 傳統IO的,NIO的一些新的特性在網絡交互方面會更加的明顯。 Java 傳統IO的弊端 ????基于JVM來實現每個通道的輪詢檢查通道狀態的方法是可行的,但仍然是有問題的,檢查每個通道...
摘要:是現在廣泛流行的代從開始學習系列之向提交代碼掘金讀完本文大概需要分鐘。為了進行高效的垃圾回收,虛擬機把堆內存劃分成新生代老年代和永久代中無永久代,使用實現三塊區域。 React Native 開源項目 - 仿美團客戶端 (Android、iOS 雙適配) - Android - 掘金推薦 React Native 學習好項目,仿照美團客戶端... 極簡 GitHub 上手教程 - 工具...
閱讀 1988·2021-11-19 09:40
閱讀 1931·2021-09-28 09:36
閱讀 2279·2021-09-22 10:02
閱讀 2724·2019-08-30 14:00
閱讀 1948·2019-08-29 15:31
閱讀 2893·2019-08-29 15:11
閱讀 2905·2019-08-29 13:04
閱讀 1080·2019-08-27 10:55