为什么这个循环是不良实践?

20

以下循环不是一个好的做法。这是因为一个 String 作为 for 循环的主要条件而不是一个 int 变量,导致了无限的 for 循环吗?此外,是否没有实例输入 'end' 来停止循环?

Scanner in = new Scanner(System.in);
int i = 0;
for (String s = in.next(); !s.equals("end"); i++) 
{
    System.out.println("The value of i is: " + i + " and you entered " + s);
}

我该如何改写它,使其符合通用的风格要求?

(这是一道过去考试题中的问题。)


29
这并不是一个坏的习惯,而更像是一个无限循环... - njzk2
@ericbn http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html - turbo
5
我认为所提出的问题没有答案,因为在给定状态下,无法将代码作为解决方案进行分析。这不是正确的代码。无论这个解决方案是否符合“良好实践”的要求,取决于在获得实际可行的解决方案时它的表现如何。 - nmclean
“还有就是没有实例可以输入 'end' 来停止循环?” Bingo。 - Radiodef
2
我为那些不得不应对如此糟糕设计的问题和作业的学生感到遗憾。 - dansalmo
显示剩余3条评论
17个回答

1
我本来想留个评论的,但是我还没有足够的声望。我看到的解释中,没有人解释为什么你的s值没有改变。
在一个典型的for循环中:
for(a=1; a<=10; a+=1) {body}

初始语句'a=1'仅在初始化时执行一次。

第三个语句'a+=1'在每个循环结束时执行一次,直到...

第二个语句'a>=10'的结果为假。

因此,一个for循环可以用“伪代码”表示如下:

    a=1         // first phrase
:LoopLabel
    {body}
    a+=1        // third phrase
    if (a<=10)  // second phrase (boolean evaluation)
        then goto LoopLabel

同样地,你的例子,在类似的假代码中可能看起来像这样:

    Scanner in = new Scanner(System.in);
    int i = 0;
    String s = in.next()
:LoopLabel
    {
        System.out.println("The value of i is: " + i + " and you entered " + s);
    }
    ++i
    if (!s.equals("end"))
        goto LoopLabel

所以你的程序成为无限循环的原因是's'的值只在进入循环时被设置,而在每个循环执行期间从未更改,这通常是不希望的。

1
这是一种不好的做法,因为它仅在下一个获得的标记是“end”时终止。它不考虑输入流结束等情况。
所以当流结束并且没有出现“end”时,您将得到`s=null`和`NullPointerException`在`s.equals("end")`处。
您可以通过将条件更改为`in.hasNext() && !"end".equals(s)`来进行更正。
此外,在初始化后,s永远不会改变。

1
如果问题是“为什么要重写它”,答案基本上就像其他人指出的那样,它目前是一个无限循环,并且作为它所站立的阅读性不太好。个人认为我会将其重写为while循环,正如其他几个人已经指出如何做到这一点,因为它使你的意图比计数器正在计算到无穷大的for循环更加清晰。对于不熟悉代码应该如何工作的人来说,无限递增很容易被认为是编写它的程序员的疏忽。

1

这种方式并不好,因为有以下两个原因:

  1. for循环的本意是遍历数据集合。
  2. for循环由迭代器的初始状态、循环条件和迭代函数组成,它们是相关联的。

for语句将两个不同的信息(流和计数器)混合在一起。即使它可以工作,也不是一个好的做法。


0
for (int i = 0; in.hasNext(); i++) {
    String s = in.next();
    if (s.equals("end")) {
        break;
    }
    ...

无限循环,或者没有循环(当s最初为"end"时)。

2
这个用 while 循环会看起来更简洁。 - Paul Samsotha
我仍然看不出使用for循环而不是while循环的原因。你同意了吗?并且已经重写了for循环来运行吗? - Stefan
我这样做(并不是很有信心)是为了限制i的范围。使用while循环似乎更规则,因为for循环的部分并不完全一致。 - Joop Eggen

0
上面的一些回答是正确的,说你写的是一个无限循环。但我想澄清一下为什么这是一个无限循环。你使用的for循环与你可能想到的另一种形式不同:
String[] stringArray = { "1", "2", "3" };
for (String s : stringArray) {
   System.out.println(s);
}

在这种情况下,变量s在每次迭代中都会用集合或数组的下一个值进行初始化。但是那种形式的for循环只适用于集合和数组,无法与像Scanner类这样的迭代器一起使用。
你正在使用的for循环形式不同之处在于初始化子句(你有String s = in.next()的地方)仅在第一次通过循环时被调用。 s在第一次设置后就不再改变。
你可以像这样重新编写:
int i = 0;
for (String s = in.next(); !s.equals("end"); s = in.next()) {
    System.out.println("The value of i is: " + i++ + " and you entered " + s);
}

但这里的另一个坏处是没有空值或结束检查。如果你在找到等于“end”的字符串之前用尽了所有字符串,那么 for 测试子句(中间的那个)在尝试调用 equals() 方法时会给你一个 NullPointerException。这绝对是不好的实践。我可能会像这样重新编写它:

int i = 0;
while (in.hasNext()) {
    String s = in.next();
    if (s.equals("end")) {
        break;
    }
    System.out.println("The value of i is: " + i++ + " and you entered " + s);
}

如果你真的想要一个 for 循环而不是 while,最好这样做:
int i = 0;
for (Scanner in = new Scanner(System.in); in.hasNext();) {
    String s = in.next();
    if (s.equals("end")) {
        break;
    }
    System.out.println("The value of i is: " + i++ + " and you entered " + s);
}

最后一种保留针对测试子句中字符串测试的变体如下:

int i = 0;
String s = "";
for (Scanner in = new Scanner(System.in);
     in.hasNext() && !s.equals("end"); 
     s = in.next()) {
    System.out.println("The value of i is: " + i++ + " and you entered " + s);
}

s.equals("end")之前,您还可以添加一个空值检查以确保完全安全。


-1

这并不是一个好的做法,可能是因为你正在比较String s与一个字符串,但你并没有比较值,而是比较了s值的内存位置。


没有内存位置比较,它是一个普通的“equals”比较,将会比较字符串的内容。 - Paŭlo Ebermann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接