首页 > 文章列表 > Java循环中的For和For-each应用对比分析

Java循环中的For和For-each应用对比分析

java for for-each
305 2023-05-04

Java循环中的For和For-each应用对比分析

for-each实现方法

For-each不是一种新语法,而是Java的语法糖。在编译时,编译器将此代码转换为迭代器实现,并将其编译为字节码。我们可以通过执行命令javap-verbose-Testforeach反编译以下编译代码:

public class TestForeach {

    List<Integer> integers;

    public void testForeach(){

        for(Integer i : integers){



        }

    }

}

获得的详细字节码如下:

public void testForeach();

    descriptor: ()V

    flags: ACC_PUBLIC

    Code:

      stack=1, locals=3, args_size=1

         0: aload_0

         1: getfield      #2                  // Field integers:Ljava/util/List;

         4: invokeinterface #3,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;

         9: astore_1

        10: aload_1

        11: invokeinterface #4,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z

        16: ifeq          32

        19: aload_1

        20: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;

        25: checkcast     #6                  // class java/lang/Integer

        28: astore_2

        29: goto          10

        32: return

      LineNumberTable:

        line 11: 0

        line 13: 29

        line 14: 32

      LocalVariableTable:

        Start  Length  Slot  Name   Signature

           29       0     2     i   Ljava/lang/Integer;

            0      33     0  this   Ltest/TestForeach;

}

此字节码的一般含义是使用getfileld命令来获取integers变量并且调用List.iterator来获取迭代器实例和调用iterator.hasNext。如果返回true,调用iterator.next方法。

请看,这是迭代器遍历集合的实现逻辑。

基准测试

现在让我们使用for循环方法和for-each方法进行测试。

public class ForLoopTest {



    public static void main(String[] args) {

        List<Integer> arrayList = new ArrayList<>();

        for (int i = 0; i < 10000000; i++) {

            arrayList.add(i);

        }



        long arrayListStartTime = System.currentTimeMillis();

        for (int i = 0; i < arrayList.size(); i++) {

            arrayList.get(i);

        }



        long arrayListCost =System.currentTimeMillis()-arrayListStartTime;

        System.out.println("ArrayList for loop traversal cost: "+ arrayListCost);



        long arrayListForeachStartTime = System.currentTimeMillis();

        for (Integer integer : arrayList) {



        }



        long arrayListForeachCost =System.currentTimeMillis()-arrayListForeachStartTime;

        System.out.println("ArrayList foreach traversal cost: "+ arrayListForeachCost);

这是测试结果:

如你所见,结果是显而易见的。对于ArrayList,使用For循环方法的性能优于For each方法。

我们可以说for循环比for-each好吗?

答案是否定的。在下一个基准测试中,我们将ArrayList更改为LinkedList。

同样,这里是测试结果。

原因分析

一些初学者可能想知道为什么ArrayList使用for循环方法遍历得更快,而LinkedList则更慢,速度也非常慢?

这由ArrayList和LinkedList数据结构决定。

ArrayList底层使用数组存储元素。数组是连续的内存空间。数据可以通过索引获得。时间复杂度为O(1),因此速度很快。

LinkedList的底层是一个双向链表。使用for循环实现遍历,每次都需要从链表的头节点开始。时间复杂度为O(n*n)。

结论

  • 使用ArrayList时,for循环方法更快,因为for-each由迭代器实现,并且需要执行并发修改验证。

  • 使用LinkedList时,for-each比for循环快得多,因为LinkedList是通过使用双向链表实现的。每个寻址都需要从头节点开始。如果我们需要遍历LinkedList,我们需要避免使用for循环。

  • 使用迭代器模式,for-each不需要关心集合的具体实现。如果需要替换集合,无需修改代码即可轻松替换。