【java8】慎用java8的foreach循環(作廢)


+警告

    這篇文章作廢掉,是由一個錯誤的測試方法得到的一個錯誤結論,后續修正結果正在測試,將貼上。

    准確測試已完成:http://www.cnblogs.com/yiwangzhibujian/p/6965114.html

  雖然java8出來很久了,但是之前用的一直也不多,最近正好學習了java8,推薦一本書還是不錯的<寫給大忙人看的javase8>。因為學習了Java8,所以只要能用到的地方都會去用,尤其是Java8的Stream,感覺用起來覺得很方便,因為點點點就出來了,而且代碼那么簡潔。現在開始慢慢深入了解java8,發現很多東西不能看表面。

  比如常規遍歷一個集合,下面給出例子:

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.當然稍微高級一點的是這樣:

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循環:

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);
  });
}

  應該沒有其他的了吧,上面六中方法,按我的使用習慣5最常用,4偶爾使用,其他的基本就不怎么用了,使用5的原因是因為方便書寫,提示就可以寫出來,偶爾使用4的原因是,5不方便計數用,下面進行性能測試,String不具備代表性,決定使用對象,簡單的一個測試類如下: 

  一個簡單的測試,內容不要太在意,簡單計算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 + "]";
  }
}
View Code

 

   運行三次取平均值,機器配置就不說了,因為我不是比較的絕對值,我是比較的這幾種方式的相對值,數據結果,趨勢圖如下:

  然后去掉表現一直很穩定的方式5和百萬級數據量以上的數據,來分析結果:

  可以得出一個非常嚇人的結果,java8的foreach每次循環的耗時竟然高達100毫秒以上,雖然它比較穩定(算是優點吧)。所以得出以下結論:

  在正常使用(數據量少於百萬以下),正常(非並行)遍歷一個集合的時候:

  • 不要使用java8的foreach,每次耗時高達100毫秒以上
  • 提前計算出大小的普通for循環,耗時最小,但是書寫麻煩
  • 增強for循環表現良好

 

測試LinkedList

    只為了驗證最后的觀點,所以只測試增強for循環和java8foreach循環:

  經過一輪測試,java8foreach一如既往的穩定高耗時,增強for循環依然很給力。

 

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();
    });
  }
}
View Code

 

  經過計算得出如下結果:

  不難發現,java8的foreach依然每次耗時100ms以上,最快的變成了增強for循環,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();
    });
  }
}
View Code

 

  結果如下:

  java8的foreach依然不負眾望,最快的是增強for循環。

 

+最終結論

    普通(數量級10W以下,非並行)遍歷一個集合(List、Set、Map)如果在意效率,不要使用java8的foreach,雖然它很方便很優雅

    任何時候使用增強for循環是你不二的選擇

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM