关于Java:看似无休止的循环终止,除非使用Soal.Out.PrtLn

Seemingly endless loop terminates, unless System.out.println is used

我有一个简单的代码,它应该是一个无止境的循环,因为x将一直在增长,并且始终比j大。

1
2
3
4
5
6
int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
   x = x + y;
}
System.out.println(y);

但实际上,它打印的是y,不会无限循环。我不明白为什么。但是,当我按以下方式调整代码时:

1
2
3
4
5
6
7
int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
    x = x + y;
    System.out.println(y);
}
System.out.println(y);

它变成了一个无止境的循环,我不知道为什么。Java是否识别它是一个无休止的循环,在第一种情况下跳过它,但必须在第二种情况下执行方法调用,这样它的行为符合预期吗?迷惑:)


这两个例子并非无穷无尽。

问题是EDCOX1×0类型在Java中的限制(或者几乎任何其他公共语言)。当x的值达到0x7fffffff时,加上任何正值都会导致溢出,x变为负值,因此低于j

第一个循环和第二个循环的区别在于,内部代码需要花费更多的时间,可能需要几分钟时间,直到x溢出。对于第一个示例,可能需要不到第二个,或者最可能的情况是,优化程序将删除代码,因为它没有任何效果。

正如在讨论中提到的,时间很大程度上取决于操作系统如何缓冲输出、是否输出到终端模拟器等,因此它可以比几分钟高很多。


由于它们被声明为int,一旦达到max值,循环将中断,因为x值将变为负数。

但是,当将system.out.println添加到循环中时,执行速度变为可见(因为输出到控制台将减慢执行速度)。但是,如果让第二个程序(循环中有syso的程序)运行足够长的时间,它应该与第一个程序(循环中没有syso的程序)具有相同的行为。


这可能有两个原因:

  • Java优化了EDCOX1×6的循环,因为在循环之后没有使用EDCOX1,1,所以简单地删除循环。您可以通过在循环后放置System.out.println(x);语句来检查这一点。

  • Java可能并没有真正地优化循环,而是正确地执行程序,并且最终EDCOX1〔1〕对于EDCOX1、0、和溢出将变得太大。整数溢出最有可能使整数x成为小于j的负数,因此它将从循环中出来并打印y的值。这也可以通过在循环后添加System.out.println(x);来检查。

  • 而且,即使在第一种情况下,最终也会发生溢出,从而将其呈现为第二种情况,因此它永远不会是真正的无限循环。


    这是一个有限循环,因为一旦x的值超过2,147,483,647(这是int的最大值),x将变为负值,并且不再大于j,无论是否打印y。

    您只需将y的值改为100000并在循环中打印y,循环很快就会中断。

    你觉得它变得无穷大的原因是System.out.println(y);使得代码的执行速度比没有任何动作要慢得多。


    它们都不是无限循环,最初j=0,只要j

    如果你在寻找一个无限循环的例子,应该像这样

    1
    2
    3
    4
    5
    int x = 6;

    for (int i = 0; x < 10; i++) {
    System.out.println("Still Looping");
    }

    因为(x)永远不会达到10的值;

    您还可以使用双for循环创建无限循环:

    1
    2
    3
    4
    5
    6
    7
    int i ;

      for (i = 0; i <= 10; i++) {
          for (i = 0; i <= 5; i++){
             System.out.println("Repeat");  
          }
     }

    这个循环是无限的,因为第一个for循环表示i<10,这是真的,所以它进入第二个for循环和第二个for循环增加(i)的值,直到它等于5。然后它进入第一个再次循环,因为i<10,进程继续重复自身,因为它在第二个for循环之后重置


    有趣的问题实际上在这两种情况下,循环并不是无穷无尽的。

    但它们之间的主要区别在于何时终止,以及x需要多长时间才能超过最大int值,即2,147,483,647值,然后达到溢出状态,循环终止。

    理解这个问题的最好方法是测试一个简单的例子并保存它的结果。

    例子:

    1
    2
    for(int i = 10; i > 0; i++) {}
    System.out.println("finished!");

    输出:

    1
    2
    finished!
    BUILD SUCCESSFUL (total time: 0 seconds)

    测试完这个无限循环后,将需要不到1秒钟的时间来终止。

    1
    2
    3
    4
    for(int i = 10; i > 0; i++) {
        System.out.println("infinite:" + i);
    }
    System.out.println("finished!");

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    infinite: 314572809
    infinite: 314572810
    infinite: 314572811
    .
    .
    .
    infinite: 2147483644
    infinite: 2147483645
    infinite: 2147483646
    infinite: 2147483647
    finished!
    BUILD SUCCESSFUL (total time: 486 minutes 25 seconds)

    在这个测试用例中,您将注意到终止和完成运行程序所花费的时间有很大的差异。

    如果你没有耐心,你会认为这个循环是无止境的,不会终止,但事实上,在i值时,终止并达到溢出状态需要几个小时。

    最后,我们在将print语句放入for循环之后得出结论,在第一种情况下,它将比不使用print语句的循环花费更多的时间。

    运行该程序所需的时间取决于您的计算机规格,特别是处理能力(处理器容量)、操作系统和正在编译该程序的IDE。

    我测试这个案例:

    联想2.7 GHz Intel Core i5

    操作系统:Windows 8.1 64x

    IDE:NetBeans 8.2

    完成这项计划大约需要8小时(486分钟)。

    此外,您还可以注意到for循环i = i + 1中的步进增量对于达到最大int值来说是非常慢的因素。

    我们可以改变这个因素,使步进更快,以便在更短的时间内测试循环。

    如果我们把i = i * 10放进去测试:

    1
    2
    3
    4
    for(int i = 10; i > 0; i*=10) {
               System.out.println("infinite:" + i);
    }
         System.out.println("finished!");

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    infinite: 100000
    infinite: 1000000
    infinite: 10000000
    infinite: 100000000
    infinite: 1000000000
    infinite: 1410065408
    infinite: 1215752192
    finished!
    BUILD SUCCESSFUL (total time: 0 seconds)

    如你所见,与前一个循环相比,它非常快

    终止并完成程序运行不到1秒。

    在这个测试例子之后,我认为应该澄清这个问题,并证明zbynek vyskovsky-kvr000的答案是正确的,同时也是这个问题的答案。