gpt4 book ai didi

java - 哪个循环更快?

转载 作者:行者123 更新时间:2023-12-01 17:32:19 25 4
gpt4 key购买 nike

我有这个循环

for (it= someCollection.iterator; it.hasNext(); )
{
//some code here
}

我把它改为:

for (it= someCollection.iterator;; )
{
if (!it.hasNext())
break;
//some code here
}

第二个代码在 eclipse 上的 junit 单元测试中运行得更快一些。第二个循环更快吗?我这样问是因为 Junit 给出的时间不太准确,但他们给出了一个大概值

最佳答案

在研究此类问题时,从 block 控制流图的角度考虑生成的字节码是有用的,其中 block 是字节码指令序列,只能从其第一条指令进入,并且只能在其之后留下最后一条指令(省略退出以简化问题)。

使用此示例代码:

    for (Iterator it = c.iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
System.out.println("Out");

您将得到以下 block 控制流程图。为了便于阅读,我已将等效的字节码放回到源代码中,但 System.out.println(it.next()); 生成的所有指令属于一个区 block ,因为你无法跳到中间或离开它。

Loop Control Flow Diagram

如果您检查compiler book ,你会发现it.hasNext()主宰System.out.println(it.next())因为你需要通过it.hasNext()前往 System.out.println(it.next()) 。边缘来自System.out.println(it.next())it.hasNext()被称为后边,因为它将一个节点链接到它的支配者之一。这就是循环的正式定义。 for 中的第一条语句-loop ( Iterator it = c.iterator() ) 实际上并不属于循环。除了声明的变量的范围之外,与此语句之前的 while 循环没有任何区别,但这在编译后并不重要。第一个 block ( it.hasNext() ) 是循环头。

像这样的第二个示例将生成相同的图表:

    for (Iterator it = c.iterator();; ) {
if (!it.hasNext()) {
break;
}
System.out.println(it.next());
}
System.out.println("Out");

主要区别是可能有一些无用的goto语句取决于编译器策略。

如果您使用javap -c查看生成的字节码对于这两个示例,您将得到以下结果(这是使用 javac 进行编译的,例如,如果使用 Eclipse 编译器进行编译,您可能会得到稍有不同的结果):

public void loop1();
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4; //Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
12: astore_2
13: aload_2
14: invokeinterface #5, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
19: ifeq 37
22: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
25: aload_2
26: invokeinterface #7, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
31: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
34: goto 13
37: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
40: ldc #9; //String Out
42: invokevirtual #10; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: return

public void loop2();
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4; //Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
12: astore_2
13: aload_2
14: invokeinterface #5, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
19: ifne 25
22: goto 40
25: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_2
29: invokeinterface #7, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
34: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
37: goto 13
40: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
43: ldc #9; //String Out
45: invokevirtual #10; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: return

唯一的区别是第一个使用 ifeq 37直接走到最后或继续下一个 block (22),而另一个使用 ifne前往 goto 之后的区 block (25,相当于另一个中的 22)并使用 goto否则走到最后。这实际上是等价的,现代 JIT 编译器应该毫无问题地优化这个小小的差异。除此之外,您的两个循环完全相同。

我不确定您是如何进行测量的,但您也应该知道这并不是因为 System.nanoTime()给你一个以纳秒为单位的结果,它的分辨率远非如此。高分辨率定时器很难实现,并且取决于硬件和操作系统。请参阅JavaDoc :

This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis().

如果您没有获得足够高的差异,则与分辨率相比,您可能不会获得显着的结果。

关于java - 哪个循环更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9774070/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com