Originally we had the FileOutputStream
in Java to create an OutputStream
that simply pipes the data into a file.
Since Java 7 we can also call Files.newOutputStream
which creates a Stream for us that behaves exactly the same (except for slight differences in "constructor"-arguments).
At least in the OpenJDK 8 the old FileOutputStream
's write method is implemented as a native method, while the OutputStream created by Files.newOutputStream
creates a ByteChannel
which is then wrapped by an OutputStream that delegates to the ByteChannel.
The second approach reads quite complicated with all the wrappers a call to write passes through. Some naive performance testing revealed though that the new approach is a tiny bit faster, but this is not very much and hardly worth mentioning. But then maybe I am not picking the best use case.
What is the reasoning behind the new ByteChannel based implementation in the OpenJDK? Is it speed? Are there cases where this will be actually noticeably faster? Why?
(I know that this is JRE-dependent and the exact implementation should not be relied on. This is primarily curiosity about the background).
while the OutputStream created by Files.newOutputStream creates a ByteChannel which is then wrapped by an OutputStream that delegates to the ByteChannel.
THis is not always true. This depends on the FileSystem
implementation! Or, more accurately, on the FileSystemProvider
implementation.
The default implementation indeed is this (note: not OpenJDK but Oracle's 8u75, so close enough):
public OutputStream newOutputStream(Path path, OpenOption... options)
throws IOException
{
int len = options.length;
Set<OpenOption> opts = new HashSet<OpenOption>(len + 3);
if (len == 0) {
opts.add(StandardOpenOption.CREATE);
opts.add(StandardOpenOption.TRUNCATE_EXISTING);
} else {
for (OpenOption opt: options) {
if (opt == StandardOpenOption.READ)
throw new IllegalArgumentException("READ not allowed");
opts.add(opt);
}
}
opts.add(StandardOpenOption.WRITE);
return Channels.newOutputStream(newByteChannel(path, opts));
}
But it does not have to be this way.
Another FileSystemProvider
implementation may very well choose to directly return an OutputStream
instead and wrap .newByteChannel()
calls using, for instance, Channels.newChannel()
.
So, the answer really is: it depends. But the OpenJDK guys are not beginners and if they have made that choice, they had a good reason.
And yes, they certainly did performance testing :p
Case in point: I have a JSR 203 implementation over Dropbox (code is quite old though); in this implementation, the FileSystemProvider
calls this method for .newOutputStream()
. And in fact .newByteChannel()
is not even supported (after talking with the experts at nio-dev, though, it appears that this is a bug; but the documentation doesn't mention that an implementation is required)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With