深入了解java8的foreach循環(huán)
雖然java8出來很久了,但是之前用的一直也不多,最近正好學(xué)習(xí)了java8,推薦一本書還是不錯的<寫給大忙人看的javase8>。因為學(xué)習(xí)了Java8,所以只要能用到的地方都會去用,尤其是Java8的Stream,感覺用起來覺得很方便,因為點點點就出來了,而且代碼那么簡潔?,F(xiàn)在開始慢慢深入了解java8,發(fā)現(xiàn)很多東西不能看表面。
比如常規(guī)遍歷一個集合,下面給出例子:
1.首先遍歷一個List
方式1.一開始是這樣的:
public static void test1(List<String> list) { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } }
方式2.當(dāng)然稍微高級一點的是這樣:
public static void test2(List<String> list) { for (int i = 0,lengh=list.size(); i < lengh; i++) { System.out.println(list.get(i)); } }
方式3.還有就是Iterator遍歷:
public static void test3(List<String> list) { Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } }
方式4.后來有了增強for循環(huán):
public static void test4(List<String> list) { for(String str:list){ System.out.println(str); } }
方式5.java8以后新增的方式:
public static void test5(List<String> list) { //list.forEach(System.out::println);和下面的寫法等價 list.forEach(str->{ System.out.println(str); }); }
方式6.還有另一種:
public static void test6(List<String> list) { list.iterator().forEachRemaining(str->{ System.out.println(str); }); }
應(yīng)該沒有其他的了吧,上面六中方法,按我的使用習(xí)慣5最常用,4偶爾使用,其他的基本就不怎么用了,使用5的原因是因為方便書寫,提示就可以寫出來,偶爾使用4的原因是,5不方便計數(shù)用,下面進(jìn)行性能測試,String不具備代表性,決定使用對象,簡單的一個測試類如下:
一個簡單的測試,內(nèi)容不要太在意,簡單計算hashCode:
package test; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Test8 { public static void main(String[] args) { List<Dog> list=new ArrayList<>(); for(int i=0;i<10;i++){ list.add(new Dog(i,"dog"+i)); } long nanoTime = System.nanoTime(); test1(list); long nanoTime1 = System.nanoTime(); test2(list); long nanoTime2 = System.nanoTime(); test3(list); long nanoTime3 = System.nanoTime(); test4(list); long nanoTime4 = System.nanoTime(); test5(list); long nanoTime5 = System.nanoTime(); test6(list); long nanoTime6 = System.nanoTime(); System.out.println((nanoTime1-nanoTime)/1000000.0); System.out.println((nanoTime2-nanoTime1)/1000000.0); System.out.println((nanoTime3-nanoTime2)/1000000.0); System.out.println((nanoTime4-nanoTime3)/1000000.0); System.out.println((nanoTime5-nanoTime4)/1000000.0); System.out.println((nanoTime6-nanoTime5)/1000000.0); } public static void test1(List<Dog> list) { for (int i = 0; i < list.size(); i++) { list.get(i).hashCode(); } } public static void test2(List<Dog> list) { for (int i = 0,lengh=list.size(); i < lengh; i++) { list.get(i).hashCode(); } } public static void test3(List<Dog> list) { Iterator<Dog> iterator = list.iterator(); while(iterator.hasNext()){ iterator.next().hashCode(); } } public static void test4(List<Dog> list) { for(Dog dog:list){ dog.hashCode(); } } public static void test5(List<Dog> list) { //list.forEach(System.out::println);和下面的寫法等價 list.forEach(dog->{ dog.hashCode(); }); } public static void test6(List<Dog> list) { list.iterator().forEachRemaining(dog->{ dog.hashCode(); }); } } class Dog{ private int age; private String name; public Dog(int age, String name) { super(); this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Dog [age=" + age + ", name=" + name + "]"; } }
運行三次取平均值,機(jī)器配置就不說了,因為我不是比較的絕對值,我是比較的這幾種方式的相對值,數(shù)據(jù)結(jié)果,趨勢圖如下:
然后去掉表現(xiàn)一直很穩(wěn)定的方式5和百萬級數(shù)據(jù)量以上的數(shù)據(jù),來分析結(jié)果:
可以得出一個非常嚇人的結(jié)果,java8的foreach每次循環(huán)的耗時竟然高達(dá)100毫秒以上,雖然它比較穩(wěn)定(算是優(yōu)點吧)。所以得出以下結(jié)論:
在正常使用(數(shù)據(jù)量少于百萬以下),正常(非并行)遍歷一個集合的時候:
•不要使用java8的foreach,每次耗時高達(dá)100毫秒以上
•提前計算出大小的普通for循環(huán),耗時最小,但是書寫麻煩
•增強for循環(huán)表現(xiàn)良好
2.再次遍歷一個Set
使用以相同的方式測試HashSet,測試方法如下:
package test; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class Test9 { public static void main(String[] args) { Set<Dog> set = new HashSet<>(); for (int i = 0; i < 10_000_000; i++) { set.add(new Dog(i, "dog" + i)); } long nanoTime = System.nanoTime(); test1(set); long nanoTime1 = System.nanoTime(); test2(set); long nanoTime2 = System.nanoTime(); test3(set); long nanoTime3 = System.nanoTime(); test4(set); long nanoTime4 = System.nanoTime(); System.out.println((nanoTime1 - nanoTime) / 1000000.0); System.out.println((nanoTime2 - nanoTime1) / 1000000.0); System.out.println((nanoTime3 - nanoTime2) / 1000000.0); System.out.println((nanoTime4 - nanoTime3) / 1000000.0); } public static void test1(Set<Dog> list) { Iterator<Dog> iterator = list.iterator(); while (iterator.hasNext()) { iterator.next().hashCode(); } } public static void test2(Set<Dog> list) { for (Dog dog : list) { dog.hashCode(); } } public static void test3(Set<Dog> list) { list.forEach(dog -> { dog.hashCode(); }); } public static void test4(Set<Dog> list) { list.iterator().forEachRemaining(dog -> { dog.hashCode(); }); } }
經(jīng)過計算得出如下結(jié)果:
不難發(fā)現(xiàn),java8的foreach依然每次耗時100ms以上,最快的變成了增強for循環(huán),Iterator遍歷和java8的iterator().forEachRemaining差不多。
3.最后遍歷Map
依然使用相同的方式測試Map集合遍歷,測試類如下:
package test; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Test10 { public static void main(String[] args) { Map<String, Dog> map = new HashMap<>(); for (int i = 0; i < 1000_000; i++) { map.put("dog" + i, new Dog(i, "dog" + i)); } long nanoTime = System.nanoTime(); test1(map); long nanoTime1 = System.nanoTime(); test2(map); long nanoTime2 = System.nanoTime(); test3(map); long nanoTime3 = System.nanoTime(); test4(map); long nanoTime4 = System.nanoTime(); System.out.println((nanoTime1 - nanoTime) / 1000000.0); System.out.println((nanoTime2 - nanoTime1) / 1000000.0); System.out.println((nanoTime3 - nanoTime2) / 1000000.0); System.out.println((nanoTime4 - nanoTime3) / 1000000.0); } public static void test1(Map<String, Dog> map) { Iterator<Map.Entry<String, Dog>> entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<String, Dog> entry = entries.next(); int code=entry.getKey().hashCode()+entry.getValue().hashCode(); } } public static void test2(Map<String, Dog> map) { for (Map.Entry<String, Dog> entry : map.entrySet()) { int code=entry.getKey().hashCode()+entry.getValue().hashCode(); } } public static void test3(Map<String, Dog> map) { for (String key : map.keySet()) { int code=key.hashCode()+map.get(key).hashCode(); } } public static void test4(Map<String, Dog> map) { map.forEach((key, value) -> { int code=key.hashCode()+value.hashCode(); }); } }
結(jié)果如下:
java8的foreach依然不負(fù)眾望,最快的是增強for循環(huán)。
最終結(jié)論
普通(數(shù)量級10W以下,非并行)遍歷一個集合(List、Set、Map)如果在意效率,不要使用java8的foreach,雖然它很方便很優(yōu)雅
任何時候使用增強for循環(huán)是你不二的選擇
以上所述是小編給大家介紹的java8的foreach循環(huán) ,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復(fù)大家的!
相關(guān)文章
淺談spring的重試機(jī)制無效@Retryable@EnableRetry
這篇文章主要介紹了淺談spring的重試機(jī)制無效@Retryable@EnableRetry,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09Java計算一個數(shù)加上100是完全平方數(shù),加上168還是完全平方數(shù)
這篇文章主要介紹了Java計算一個數(shù)加上100是完全平方數(shù),加上168還是完全平方數(shù),需要的朋友可以參考下2017-02-02Spring boot + LayIM + t-io 實現(xiàn)文件上傳、 監(jiān)聽用戶狀態(tài)的實例代碼
這篇文章主要介紹了Spring boot + LayIM + t-io 實現(xiàn)文件上傳、 監(jiān)聽用戶狀態(tài)的實例代碼,需要的朋友可以參考下2017-12-12SpringBoot+Vue+JWT的前后端分離登錄認(rèn)證詳細(xì)步驟
這篇文章主要介紹了SpringBoot+Vue+JWT的前后端分離登錄認(rèn)證,其實創(chuàng)建后端springboot工程也很簡單,本文安裝idea步驟一步步給大家詳細(xì)介紹,需要的朋友可以參考下2021-09-09