• Java 中 for 和 foreach 哪个性能高?


    作为程序员每天除了写很多 if else 之外,写的最多的也包含 for 循环了,都知道我们 Java 中常用的 for 循环有两种方式,一种是使用 for loop,另一种是使用 foreach,那如果问你,这两种方式哪一种效率最高,你的回答是什么呢?

    首先我们先通过代码来实际测试一下,在计算耗时之前我们先创建一个大小集合,然后通过不断的获取集合中的内容来测试耗时。

    1. package com.example.demo;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. public class ForTest {
    5.   public static void main(String[] args) {
    6.     //获取一个指定大小的 List 集合
    7.     List<Integer> list = getList(1000000);
    8.     // 开启 for loop 耗时计算
    9.     long startFor = System.currentTimeMillis();
    10.     for (int i = 0; i < list.size(); i++) {
    11.       Integer integer = list.get(i);
    12.     }
    13.     long costFor = System.currentTimeMillis() - startFor;
    14.     System.out.println("for loop cost for ArrayList:" + costFor);
    15.     // forEach 耗时计算
    16.     long forEachStartTime = System.currentTimeMillis();
    17.     for (Integer integer : list) {
    18.     }
    19.     long forEachCost = System.currentTimeMillis() - forEachStartTime;
    20.     System.out.println("foreach cost for ArrayList:" + forEachCost);
    21.   }
    22.   public static List<Integer> getList(int size) {
    23.     List<Integer> list = new ArrayList<>();
    24.     for (int i = 0; i < size; i++) {
    25.       list.add(i);
    26.     }
    27.     return list;
    28.   }
    29. }

    简单说明一下上面的带,先创建一个 List ,然后通过两种方式的遍历来计算耗时,根据集合的大小不同,我们进行运行会得到下面的一些测试数据,不同人的机器上面运行的时间会不一定,不过差距应该也不会太大。

    size=10000100000100000010000000
    for loop121012
    for each131734

    通过上面的测试结果我们可以发现,在集合相对较小的情况下,for loop 和 foreach 两者的耗时基本上没有什么差别,当集合的数据量相对较大的时候,可以明显看的出来,for loop 的效率要比 foreach 的效率高。

    至于为什么在大数据量的情况下 forEach 的效率要比 for 低,我们就要看下 forEach 的原理了。forEach 其实不是一种新的语法,而是一种 Java 的语法糖。在编译时,编译器会将这段代码转换成迭代器实现,并编译成字节码,我们可以再简单的看个 case,来实际看下字节码信息。

    我们再编写一个简单的类,代码如下

    1. package com.example.demo;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. /**
    5.  * <br>
    6.  * <b>Function:</b><br>
    7.  * <b>Author:</b>@author ziyou<br>
    8.  * <b>Date:</b>2022-06-26 13:06<br>
    9.  * <b>Desc:</b><br>
    10.  */
    11. public class ForEachTest {
    12.   List<Integer> list;
    13.   public void main(String[] args) {
    14.     for (Integer integer : list) {
    15.     }
    16.   }
    17. }

    通过 javac ForEachTest.java 编译成 class 文件,再通过 javap -v ForEachTest 反编译,我们就会得到下面的字节码内容:

    1. Classfile /Users/silence/Downloads/demo/src/test/java/com/example/demo/ForEachTest.class
    2.   Last modified 2022-6-26; size 643 bytes
    3.   MD5 checksum 9cf01f7c8c87c2b4d62c39d437025b7f
    4.   Compiled from "ForEachTest.java"
    5. public class com.example.demo.ForEachTest
    6.   minor version: 0
    7.   major version: 52
    8.   flags: ACC_PUBLIC, ACC_SUPER
    9. Constant pool:
    10.    #1 = Methodref          #8.#23         // java/lang/Object."<init>":()V
    11.    #2 = Fieldref           #7.#24         // com/example/demo/ForEachTest.list:Ljava/util/List;
    12.    #3 = InterfaceMethodref #25.#26        // java/util/List.iterator:()Ljava/util/Iterator;
    13.    #4 = InterfaceMethodref #27.#28        // java/util/Iterator.hasNext:()Z
    14.    #5 = InterfaceMethodref #27.#29        // java/util/Iterator.next:()Ljava/lang/Object;
    15.    #6 = Class              #30            // java/lang/Integer
    16.    #7 = Class              #31            // com/example/demo/ForEachTest
    17.    #8 = Class              #32            // java/lang/Object
    18.    #9 = Utf8               list
    19.   #10 = Utf8               Ljava/util/List;
    20.   #11 = Utf8               Signature
    21.   #12 = Utf8               Ljava/util/List<Ljava/lang/Integer;>;
    22.   #13 = Utf8               <init>
    23.   #14 = Utf8               ()V
    24.   #15 = Utf8               Code
    25.   #16 = Utf8               LineNumberTable
    26.   #17 = Utf8               main
    27.   #18 = Utf8               ([Ljava/lang/String;)V
    28.   #19 = Utf8               StackMapTable
    29.   #20 = Class              #33            // java/util/Iterator
    30.   #21 = Utf8               SourceFile
    31.   #22 = Utf8               ForEachTest.java
    32.   #23 = NameAndType        #13:#14        // "<init>":()V
    33.   #24 = NameAndType        #9:#10         // list:Ljava/util/List;
    34.   #25 = Class              #34            // java/util/List
    35.   #26 = NameAndType        #35:#36        // iterator:()Ljava/util/Iterator;
    36.   #27 = Class              #33            // java/util/Iterator
    37.   #28 = NameAndType        #37:#38        // hasNext:()Z
    38.   #29 = NameAndType        #39:#40        // next:()Ljava/lang/Object;
    39.   #30 = Utf8               java/lang/Integer
    40.   #31 = Utf8               com/example/demo/ForEachTest
    41.   #32 = Utf8               java/lang/Object
    42.   #33 = Utf8               java/util/Iterator
    43.   #34 = Utf8               java/util/List
    44.   #35 = Utf8               iterator
    45.   #36 = Utf8               ()Ljava/util/Iterator;
    46.   #37 = Utf8               hasNext
    47.   #38 = Utf8               ()Z
    48.   #39 = Utf8               next
    49.   #40 = Utf8               ()Ljava/lang/Object;
    50. {
    51.   java.util.List<java.lang.Integer> list;
    52.     descriptor: Ljava/util/List;
    53.     flags:
    54.     Signature: #12                          // Ljava/util/List<Ljava/lang/Integer;>;
    55.   public com.example.demo.ForEachTest();
    56.     descriptor: ()V
    57.     flags: ACC_PUBLIC
    58.     Code:
    59.       stack=1locals=1, args_size=1
    60.          0: aload_0
    61.          1: invokespecial #1                  // Method java/lang/Object."<init>":()V
    62.          4return
    63.       LineNumberTable:
    64.         line 130
    65.   public void main(java.lang.String[]);
    66.     descriptor: ([Ljava/lang/String;)V
    67.     flags: ACC_PUBLIC
    68.     Code:
    69.       stack=1locals=4, args_size=2
    70.          0: aload_0
    71.          1: getfield      #2                  // Field list:Ljava/util/List;
    72.          4: invokeinterface #3,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
    73.          9: astore_2
    74.         10: aload_2
    75.         11: invokeinterface #4,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
    76.         16: ifeq          32
    77.         19: aload_2
    78.         20: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
    79.         25: checkcast     #6                  // class java/lang/Integer
    80.         28: astore_3
    81.         29: goto          10
    82.         32return
    83.       LineNumberTable:
    84.         line 170
    85.         line 1929
    86.         line 2032
    87.       StackMapTable: number_of_entries = 2
    88.         frame_type = 252 /* append */
    89.           offset_delta = 10
    90.           locals = [ class java/util/Iterator ]
    91.         frame_type = 250 /* chop */
    92.           offset_delta = 21
    93. }
    94. SourceFile: "ForEachTest.java"

    反编译的内容很多,不一一解释,可以看到这个字节码的一般含义是使用 getfield 命令获取变量,并调用 List.iterator 获取迭代器实例再调用 iterator.hasNext,如果返回 true,则调用 iterator.next 方法,这是迭代器遍历集合的实现逻辑。

    写到这里有小伙伴就要问了,那以后遇到 List 集合我就用 for loop 了,不用 foreach了,毕竟前者的效率更好。那么接下来我们再看一个 case,这里我们把 ArrayList 换成 LinkedList,代码如下:

    1.   public static List<Integer> getList(int size) {
    2.     List<Integer> list = new LinkedList<>();
    3.     for (int i = 0; i < size; i++) {
    4.       list.add(i);
    5.     }
    6.     return list;
    7.   }
    size=100010000100000
    for loop271297654
    For each2215

    从上面的数据可以很明显的看到,当在处理 LinkedList 的时候,for loop 明显就慢很多了。相信具体的原因大家也知道,ArrayList 底层是基于数组结构的,所以使用 for loop 操作起来会很快,时间复杂度是 O(1),但是 LinkedList 底层是链表结构,此时如果在想通过索引来操作数据,时间复杂度将是 O (n*n)

    所以具体使用哪种循环方式以及具体需要使用哪种数据结构,都需要根据实际的业务情况来选择,任何一种方案的存在都是合理的,你小伙你们认为呢?欢迎在评论区留言讨论。

  • 相关阅读:
    【JavaWeb】Tomcat部署Web项目以及Maven工具的使用
    48 - 同名覆盖引发的问题
    C++ PrimerPlus 复习 第三章 处理数据
    Rust 中文社区 2024 调查问卷
    scala
    Nginx + Tomcat 搭建负载均衡、动态分离
    Python之读写文件
    《大数据之路:阿里巴巴大数据实践》-第1篇 数据技术篇 -第6章 数据服务
    MyBatis学习:使用Map的方法传递参数
    22-07-05 七牛云存储图片、用户头像上传
  • 原文地址:https://blog.csdn.net/java_beautiful/article/details/125539556