【java8】慎用java8的foreach循环

  虽然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 + "]";
  }
}

  运行三次取平均值,机器配置就不说了,因为我不是比较的绝对值,我是比较的这几种方式的相对值,数据结果,趋势图如下:

  然后去掉表现一直很稳定的方式5和百万级数据量以上的数据,来分析结果:

  可以得出一个非常吓人的结果,java8的foreach每次循环的耗时竟然高达100毫秒以上,虽然它比较稳定(算是优点吧)。所以得出以下结论:

  在正常使用(数据量少于百万以下),正常(非并行)遍历一个集合的时候:

  • 不要使用java8的foreach,每次耗时高达100毫秒以上
  • 提前计算出大小的普通for循环,耗时最小,但是书写麻烦
  • 增强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();
    });
  }
}

  经过计算得出如下结果:

  不难发现,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();
    });
  }
}

  结果如下:

  java8的foreach依然不负众望,最快的是增强for循环。

+最终结论

    普通(数量级10W以下,非并行)遍历一个集合(List、Set、Map)如果在意效率,不要使用java8的foreach,虽然它很方便很优雅

    任何时候使用增强for循环是你不二的选择

时间: 2025-01-02 17:32:15

【java8】慎用java8的foreach循环的相关文章

为什么阿里禁止在 foreach 循环里进行元素的 remove/add 操作

在阿里巴巴Java开发手册中,有这样一条规定:但是手册中并没有给出具体原因,本文就来深入分析一下该规定背后的思考.1.foreach循环foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数组或集合中的元素.Java语言从JDK 1.5.0开始引入foreach循环.在遍历数组.集合方面,foreach为开发人员提供了极大的方便.通常也被称之为增强for循环.foreach 语法格式如下: for(元素类型t 元素变量x : 遍历对象obj){ 引

为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作

这个问题我在实际写代码中也遇到过,当时还很疑惑,刚看到这里有一些解释得挺清楚,记录一下: 原文地址为:https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650123395&idx=1&sn=b089eac4f561f58ee8a92602db17f577&chksm=f36bb1a2c41c38b4f36c1a84f205e188a6a8aa2684f316e4dcb8d1162b6e94b970c670b2e5b

foreach循环语句

一.foreach循环语句介绍: 1.格式: for(类型 变量名称:被遍历数组或集合){ 其他操作(输出操作) } 2.作用: 主要是用于简化书写 二.foreach循环语句遍历数组: public class oneHundredAndEight_oneHundredAndNine { public static void main(String[] args) { String[] names = {"萌萌","纯纯","爱爱","

迭代器、foreach循环、泛型集合

集合的迭代 语法:Iterator<Object> it=集合.iterator(); while(it.hasNext()){ Object obj=it.next(); } is.hasNext(); //判断是否有下一个元素 it.next(); //移动指针,返回指针指向元素 注意:集合在遍历的过程中不能进行修改,如果进行了修改操作,那么就会抛出ConcurrentModificationException异常, 如果需要进行删除,使用迭代器的it.remove()方法 foreach

第46条:for-each循环优先于传统的for循环

for-each循环通过完全隐藏迭代器或者索引变量,避免混乱和出错的可能,适用于集合和数组和任何实现Iterable接口的对象. 使用传统for循环,容易出错: enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX } Collection<Face> faces = Arrays.asList(Face.values()); for(Iterator<Face> i = faces.iterator(); i.hasNext();) for(I

for foreach循环

//for循环 int[] nums = {1,2,3,4,5,6,8,7,9,10 }; for (int i = 0; i < nums.Length;i++ ) { Console.WriteLine(nums[i]); } Console.ReadKey(); //foreach循环 int[] nums = { 1, 2, 3, 4, 5, 6, 8, 7, 9, 10 }; foreach (var item in nums) { Console.WriteLine(item); }

For-Each循环

•For-Each循环的加入简化了集合的遍历 •其語法如下 –for(type element : array) { System.out.println(element).... } •参见程序 ForTest.java 当遍历集合或数组时,如果需要访问集合或数组的下标,那么最好使用旧式的方式来实现循环或遍历,而不要使用增强的for循环,因为它丢失了下标信息. 1 import java.util.ArrayList; 2 import java.util.Collection; 3 impo

JAVA中的for-each循环与迭代

在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为 "foreach" 语句的目标,而此接口中的唯一方法,实现的就是返回一个在一组 T 类型的元素上进行迭代的迭代器. 一.迭代器Iterator 接口:Iterator<T> 1 public interface Iterator<E>{ 2 3 boolean h

JDK5新特性--forEach循环

1.增强for循环概述 简化数组和Collection集合的遍历 2.格式 for(元素数据类型 变量:数组或者Collection集合){     使用变量集合,该变量就是元素 } package cn; public class ForDemo { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; for (int i = 0; i < arr.length; i++) { System.out.println

2.24 Java基础总结 ①for-each循环②继承概念③全类名④方法重写

①for-each循环②继承概念③全类名④方法重写 一.for-each循环依次遍历集合中每个元素的时候,for循环的一种int [] a = {1,2,3};for(int result : a){ System.out.println(result);} 全部依次遍历,但是没有细节的处理 二.继承is-a的关系子类继承来自父类中的所有属性和方法,呗继承的受访问修饰符限制但是构造不能被继承,父类的构造在构造是会起作用优点:①复用性②扩展父类属性和方法 Java是单继承,一个类只有一个父类单继承