【问题标题】:A better Java loop?更好的 Java 循环?
【发布时间】:2011-08-29 14:37:15
【问题描述】:

我有一段代码可以读取InputStream 并将内容写入OutputStream

BufferedInputStream in = new BufferedInputStream(...);
FileOutputStream outStream = new FileOutputStream outStream(...);

int read = in.read(buffer, 0, bufferSize);
while (read != -1) {
    outStream.write(buffer, 0, read);
    read = in.read(buffer, 0, bufferSize);
}

它可以工作,但我不喜欢它,因为变量read 是在循环外声明的,而read() 方法被写了两次。 修改后的版本:

 for (int read = 0; read != -1; read = in.read(buffer, 0, bufferSize)) {
      outStream.write(buffer, 0, read);
 }

它看起来更好但不够好,因为第一次迭代在 read=0 时是无用的(并且可能有害)。

你有更好的解决方案吗?

【问题讨论】:

  • 恕我直言,第一个版本比第二个版本更具可读性。
  • 或者你可以使用 commons-io 并将整个内容替换为 IOUtils.copy(in, outStream);

标签: java loops for-loop while-loop


【解决方案1】:

就我个人而言,对于这类事情,我打破了正常的“条件无副作用”规则:

int bytesRead;
while ((bytesRead = in.read(buffer, 0, bufferSize)) != -1)
{
    outStream.write(buffer, 0, bytesRead);
}

编辑:如上所述,它确实涉及在循环外声明read,但它只调用一次read()。我从来没有发现它是一个问题——虽然我通常更喜欢用尽可能小的范围声明变量,但这更像是一个一般的清洁问题。如果您想进一步限制范围,您可以将整个内容放在大括号中,或者将其提取到自己的方法中,例如 Alan 的方法。以下是我的实现方式:

public static void copyStream(InputStream input, OutputStream output)
    throws IOException {
  byte[] buffer = new byte[1024 * 16]; // Reasonable general size

  int bytesRead;
  while ((bytesRead = in.read(buffer, 0, buffer.length)) != -1) {
    outStream.write(buffer, 0, bytesRead);
  }
}

或者,您可以提供缓冲区长度作为参数。请注意,这现在可以进入实用程序库,您无需再次编写代码。

或者,您可以使用它已经在其他实用程序库中可用的事实,例如 Guava 作为 ByteStreams.copy

【讨论】:

  • 这仍然需要在循环外声明bytesRead,这显然是不想要的。
  • 嗯,这个答案通常与 Mat 中的答案相同(两者几乎同时出现)。但是为什么这个有更多的投票呢?只因为是乔恩?或者你读过一些我没见过的东西?
【解决方案2】:

你可以这样做:

BufferedInputStream in = new BufferedInputStream(...);
FileOutputStream outStream = new FileOutputStream outStream(...);

while (true) { // can i use for(;;) in Java ???
    int read = in.read(buffer, 0, bufferSize);
    if (read == -1) break;
    outStream.write(buffer, 0, read);
}

不过,它使用break。有人说break 风格不好/不太好。

【讨论】:

  • 我的老 CS 老师总是告诉我:“这就像带着弹射座椅下飞机”。恕我直言:这取决于循环。在这种情况下,使用 break 没有任何意义,而 while 是更好的选择(请参阅其他答案)。
  • @Simon: break这里不是guard clause的一种吗?
  • 好吧,严格来说,这个选项可以避免read 拥有比必要更大的范围。我同意这不是很重要。
  • 是的,for (;;) 在 Java 中有效 - 编译为与 while (true) 相同
  • @wang.xueqiang 是的,但while(true) 是一种非常糟糕的风格。只需使用 Jon Skeet 提供的答案,这是常用的方法。
【解决方案3】:

这不是很出色,但是通过一个简单的块,您可以阻止read 变量在稍后的方法中被访问:

BufferedInputStream in = new BufferedInputStream(...);
FileOutputStream outStream = new FileOutputStream outStream(...);

{
    int read = in.read(buffer, 0, bufferSize);
    while (read != -1)
    {
        outStream.write(buffer, 0, read);
        read = in.read(buffer, 0, bufferSize);
    }
}

// ...rest of your code

...但我同意,我也经常想要一个 while 循环,其中要测试的值在循环内初始化。据我所知,这是不可能的。

另一种方法是使用extract method design pattern 将该循环拉出到一个完全独立的方法中,即

public void yourMethod() {
    BufferedInputStream in = new BufferedInputStream(...);
    FileOutputStream outStream = new FileOutputStream outStream(...);

    this.writeToOutputStream(in, outStream);
}

private void writeToOutputStream(InputStream in, OutputStream outStream) {
    int read = in.read(buffer, 0, bufferSize);
    while (read != -1)
    {
        outStream.write(buffer, 0, read);
        read = in.read(buffer, 0, bufferSize);
    }
}

【讨论】:

    【解决方案4】:

    这种形式比较常见:

    while ((read = in.read(buffer, 0, bufferSize)) != -1) {
      ...
    }
    

    但不太清楚 IMO。

    【讨论】:

    • 不推荐我的第二个版本,是吗?
    • @wang:在我看来,它的可读性更差,您指出第一次迭代是错误的是正确的。这个版本很常见,你会看到很多,所以其他阅读你的代码的人应该不会太困惑。
    猜你喜欢
    • 2012-09-10
    • 2019-04-17
    • 2013-06-28
    • 1970-01-01
    • 1970-01-01
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多