A better Java loop?
I have a piece of code to read a InputStream
and write the content into an OutputStream
:
BufferedInputStream in = new BufferedI开发者_运维问答nputStream(...);
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);
}
It works, but i don't like it since the variable read
is declared out of the loop, and read()
method is written twice.
The revised version:
for (int read = 0; read != -1; read = in.read(buffer, 0, bufferSize)) {
outStream.write(buffer, 0, read);
}
It looks better but not good enough because the first iteration is useless (and maybe harmful) with read=0.
Do you have a better solution?
Personally I break the normal "no side-effect in a condition" rule for this sort of thing:
int bytesRead;
while ((bytesRead = in.read(buffer, 0, bufferSize)) != -1)
{
outStream.write(buffer, 0, bytesRead);
}
EDIT: As noted, it does involve declaring read
outside the loop, but it only calls read()
once. I've never found it to be a problem - while I generally prefer to declare variables with as small a scope as possible, that's more a general cleanliness thing. If you want to limit the scope further you can put the whole thing in braces, or extract it to its own method, like Alan's approach. Here's how I'd implement it though:
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);
}
}
Alternatively you could provide the buffer length as a parameter. Note that this can now go off into a utility library, and you need never write the code again.
Alternatively, you could use the fact that it's already available in other utility libraries, such as Guava as ByteStreams.copy
You can do it this way:
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);
}
It uses a break
, though. Some people say break
is bad/not so good style.
It's not brilliant, but with a simple block, you can stop the read
variable from being accessed later in the method:
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
...but I agree, I've also often wanted a while loop where the value to be tested is initialized inside the loop. As far as I know, it's not possible.
Another way to do it is to use the extract method design pattern to pull out that loop in to a completely separate method, i.e.
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);
}
}
This form is rather usual:
while ((read = in.read(buffer, 0, bufferSize)) != -1) {
...
}
but not so good for clarity IMO.
精彩评论