用了那么久的 Java For 循环,你知道哪种方式效率最高吗?

您所在的位置:网站首页 java控制台在哪里 用了那么久的 Java For 循环,你知道哪种方式效率最高吗?

用了那么久的 Java For 循环,你知道哪种方式效率最高吗?

2023-03-11 08:54| 来源: 网络整理| 查看: 265

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

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

package com.example.demo; import java.util.ArrayList; import java.util.List; /** * * Function: * Author:@author ziyou * Date:2022-06-26 12:22 * Desc:无 */ public class ForTest { public static void main(String[] args) { //获取一个指定大小的 List 集合 List list = getList(1000000); // 开启 for loop 耗时计算 long startFor = System.currentTimeMillis(); for (int i = 0; i < list.size(); i++) { Integer integer = list.get(i); } long costFor = System.currentTimeMillis() - startFor; System.out.println("for loop cost for ArrayList:" + costFor); // forEach 耗时计算 long forEachStartTime = System.currentTimeMillis(); for (Integer integer : list) { } long forEachCost = System.currentTimeMillis() - forEachStartTime; System.out.println("foreach cost for ArrayList:" + forEachCost); } public static List getList(int size) { List list = new ArrayList(); for (int i = 0; i < size; i++) { list.add(i); } return list; } }

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

size=10000100000100000010000000for loop121012for each131734

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

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

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

package com.example.demo; import java.util.ArrayList; import java.util.List; /** * * Function: * Author:@author ziyou * Date:2022-06-26 13:06 * Desc:无 */ public class ForEachTest { List list; public void main(String[] args) { for (Integer integer : list) { } } }

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

Classfile /Users/silence/Downloads/demo/src/test/java/com/example/demo/ForEachTest.class Last modified 2022-6-26; size 643 bytes MD5 checksum 9cf01f7c8c87c2b4d62c39d437025b7f Compiled from "ForEachTest.java" public class com.example.demo.ForEachTest minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #8.#23 // java/lang/Object."":()V #2 = Fieldref #7.#24 // com/example/demo/ForEachTest.list:Ljava/util/List; #3 = InterfaceMethodref #25.#26 // java/util/List.iterator:()Ljava/util/Iterator; #4 = InterfaceMethodref #27.#28 // java/util/Iterator.hasNext:()Z #5 = InterfaceMethodref #27.#29 // java/util/Iterator.next:()Ljava/lang/Object; #6 = Class #30 // java/lang/Integer #7 = Class #31 // com/example/demo/ForEachTest #8 = Class #32 // java/lang/Object #9 = Utf8 list #10 = Utf8 Ljava/util/List; #11 = Utf8 Signature #12 = Utf8 Ljava/util/List; #13 = Utf8 #14 = Utf8 ()V #15 = Utf8 Code #16 = Utf8 LineNumberTable #17 = Utf8 main #18 = Utf8 ([Ljava/lang/String;)V #19 = Utf8 StackMapTable #20 = Class #33 // java/util/Iterator #21 = Utf8 SourceFile #22 = Utf8 ForEachTest.java #23 = NameAndType #13:#14 // "":()V #24 = NameAndType #9:#10 // list:Ljava/util/List; #25 = Class #34 // java/util/List #26 = NameAndType #35:#36 // iterator:()Ljava/util/Iterator; #27 = Class #33 // java/util/Iterator #28 = NameAndType #37:#38 // hasNext:()Z #29 = NameAndType #39:#40 // next:()Ljava/lang/Object; #30 = Utf8 java/lang/Integer #31 = Utf8 com/example/demo/ForEachTest #32 = Utf8 java/lang/Object #33 = Utf8 java/util/Iterator #34 = Utf8 java/util/List #35 = Utf8 iterator #36 = Utf8 ()Ljava/util/Iterator; #37 = Utf8 hasNext #38 = Utf8 ()Z #39 = Utf8 next #40 = Utf8 ()Ljava/lang/Object; { java.util.List list; descriptor: Ljava/util/List; flags: Signature: #12 // Ljava/util/List; public com.example.demo.ForEachTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 13: 0 public void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC Code: stack=1, locals=4, args_size=2 0: aload_0 1: getfield #2 // Field list:Ljava/util/List; 4: invokeinterface #3, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 9: astore_2 10: aload_2 11: invokeinterface #4, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 16: ifeq 32 19: aload_2 20: invokeinterface #5, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 25: checkcast #6 // class java/lang/Integer 28: astore_3 29: goto 10 32: return LineNumberTable: line 17: 0 line 19: 29 line 20: 32 StackMapTable: number_of_entries = 2 frame_type = 252 /* append */ offset_delta = 10 locals = [ class java/util/Iterator ] frame_type = 250 /* chop */ offset_delta = 21 } SourceFile: "ForEachTest.java"

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

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

public static List getList(int size) { List list = new LinkedList(); for (int i = 0; i < size; i++) { list.add(i); } return list; }size=100010000100000for loop271297654For each2215

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

所以具体使用哪种循环方式以及具体需要使用哪种数据结构,都需要根据实际的业务情况来选择,任何一种方案的存在都是合理的



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3