Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Files.newOutputStream vs FileOutputStream

Tags:

java

openjdk

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).

like image 626
yankee Avatar asked Oct 30 '22 09:10

yankee


1 Answers

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)

like image 97
fge Avatar answered Nov 09 '22 22:11

fge