【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 + "]";
- }
- }
运行三次取平均值,机器配置就不说了,因为我不是比较的绝对值,我是比较的这几种方式的相对值,数据结果,趋势图如下:
然后去掉表现一直很稳定的方式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();
- });
- }
- }
经过计算得出如下结果:
不难发现,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循环是你不二的选择
【java8】慎用java8的foreach循环的更多相关文章
- 为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作--java.util.ConcurrentModificationException
摘要 foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数组或集合中的元素. 在阿里巴巴Java开发手册中,有这样一条规定: 但是手册中并没有给出具体 ...
- 为什么禁止在 foreach 循环里进行元素的 remove/add 操作
首先看下边一个例子,展示了正确的做法和错误的错发: 这是为什么呢,具体原因下面进行详细说明: 1.foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数 ...
- 有关集合的foreach循环里的add/remove
转自:Hollis(微信号:hollischuang) 在阿里巴巴Java开发手册中,有这样一条规定: 但是手册中并没有给出具体原因,本文就来深入分析一下该规定背后的思考. 1 .foreach循环 ...
- 禁止foreach循环使用remove/add----快速失败
阿里巴巴开发手册中有一条: 7[强制]不要在 foreach 循环里进行元素的 remove / add 操作. remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterato ...
- [Java8教程]Java8新特性进阶集合
Java8新特性进阶集合 基于 AOP 抽离方法的重复代码 Java8:当 Lambda 遇上受检异常 Java8:对字符串连接的改进 Java8:Java8 中 Map 接口的新方法 Java8:当 ...
- JAVA中的for-each循环与迭代
在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为 ...
- foreach循环 Java
第一次遇到foreach循环,是在PHP的数组中,同样,在Java数组中,也遇到了foreach循环,都是用来遍历数组(集合).遍历数组,首先想到的一般都是用while,do while,for循环, ...
- 集合框架遍历方式之——for-each循环
从Java5起,在Java中有了for-each循环,可以用来循环遍历collection和array.Foreach循环允许你在无需保持传统for循环中的索引,或在使用iterator /ListI ...
- Java语法糖1:可变长度参数以及foreach循环原理
语法糖 接下来几篇文章要开启一个Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的 ...
- 巧用array_map()和array_reduce()替代foreach循环
1.array_reduce( $arr , callable $callback ) 使用回调函数迭代地将数组简化为单一的值. 其中$arr 为输入数组,$callback($result , $v ...
随机推荐
- Android批量验证渠道、版本号
功能:可校验单个或目录下所有apk文件的渠道号.版本号使用说明:1.copy需要校验的apk文件到VerifyChannelVersion目录下2.双击运行VerifyChannelVersion.b ...
- C#各个版本中的新增特性详解
序言 自从2000年初期发布以来,c#编程语言不断的得到改进,使我们能够更加清晰的编写代码,也更加容易维护我们的代码,增强的功能已经从1.0搞到啦7.0甚至7.1,每一次改过都伴随着.NET Fram ...
- less学习笔记(二)
1.作用域:基本与javascrip的作用域相似,即先找局部变量,后找全局变量.找变量时遵循就近原则. 2.条件表达式:.mixin (@a) when (lightness(@a) >= 50 ...
- Unity3D 正六边形,环状扩散,紧密分布,的程序
最近在做一个正六边形的游戏,被一开始的布局难倒了. 需求:中心有个正六边形,输入围绕中心扩散的环数,自动创建和摆放. 大概就是这样的吧,我觉得这个非常轻松的就可以搞定了.啊~~~~~啊~~~ 五环~~ ...
- TomCat杀进程
有时候当你的tomcat启动时会发现 因为报以下的错误: "Several ports ( 8080, 8009) required by Tomcat v6.0 Server at loc ...
- 火车站点城市查询(appserv 服务器练习)
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- C#事物
执行ADO.NET事务包含四个步骤,分别为: ①调用SqlConnection对象的BeginTransaction()方法,(只调用这个方法前,要打开数据库连接,否则将会出现异常) 创建一个SqlT ...
- Linux 系统管理06--磁盘管理
Linux系统管理06——磁盘管理 一.磁盘结构 1.硬盘的物理结构 盘片:硬盘有多个盘片,每个盘片2面 磁头:每面一个磁头 2.硬盘的数据结构 扇区:盘片被分为多个扇形区域,每个扇形区存放512字节 ...
- C语言学习第九章
学习C语言的最后一节课了,原因嘛上一章的末尾说过了,其实写这篇博客的时候以后开始学习Java一个多月了,一直因为各种各样的原因没有坚持做到每天一篇学习记录,可能主要因为懒吧....也有点笨,Java的 ...
- Javascript中call和apply
在上一章http://www.cnblogs.com/meiyh/p/6207671.html 我有提到javascript很重要的this关键字,现在我们结合这个关键字使用的下面的两个关键字: ca ...