2016-02-19 33 views
3

最初我们用Java中的FileOutputStream创建了一个OutputStream,它只是将数据传输到一个文件中。Files.newOutputStream vs FileOutputStream

从Java 7开始,我们也可以调用Files.newOutputStream,它为我们创建了一个行为完全相同的Stream(“构造函数”参数中的细微差别除外)。

至少在OpenJDK的8旧FileOutputStream“s的写入方法被实现为一个本机方法,同时通过Files.newOutputStream创建的OutputStream创建一个ByteChannel,然后通过一个OutputStream包裹该委托给了ByteChannel。

第二种方法对于写入调用所经过的所有包装来说相当复杂。一些天真的性能测试表明,虽然新方法速度稍微快了一点,但这不是很多,也不值得一提。但是,也许我没有选择最好的用例。

OpenJDK中新的基于ByteChannel的实现的原因是什么?速度快吗?有没有这种情况会明显更快?为什么?

(我知道这是JRE依赖的,不应该依赖确切的实现,这主要是关于背景的好奇心)。

+0

我认为一切都在“一些天真的性能测试”。你是如何测试它的?你可以发布你的基准代码吗? – Tunaki

+0

这种性能测试非常依赖操作系统;我怀疑OpenJDK家伙在决定实现这个实现之前,在尽可能多的系统上进行了广泛的性能测试。 – fge

+0

此外,只是为了澄清:这个问题与一个人想要一个'File'和另一个'Path'的事实没有关系,对吧? – fge

回答

3

而Files.newOutputStream创建的OutputStream创建一个ByteChannel,然后由一个OutputStream包装,该OutputStream委托给ByteChannel。

这并非总是如此。这取决于FileSystem的实现!或者,更准确地说,在FileSystemProvider实施。

默认实现确实是这样的(注意:不是的OpenJDK但Oracle的8u75,所以非常接近):

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)); 
} 

它并不一定是这样

另一个FileSystemProvider的实现可能会选择直接返回OutputStream,而使用例如Channels.newChannel()来包装.newByteChannel()调用。

所以,答案真的是:这取决于。但OpenJDK家伙不是初学者,如果他们做出了这个选择,他们有一个很好的理由。

是的,他们肯定没有性能测试:对


个例子:我有一个JSR 203实现了Dropbox的(代码是很老虽然);在此实施中,FileSystemProvider针对.newOutputStream()调用this method。事实上.newByteChannel()甚至没有被支持(在与nio-dev的专家交谈之后,看起来这是一个错误;但文档没有提到需要实现)

+0

“但OpenJDK的家伙不是初学者,如果他们做出了这个选择,他们有一个很好的理由。” - >是的,我希望如此。但最终的问题不在于理由是好还是坏,但问题是:理由是什么? – yankee

+0

推理是,当涉及到任何'* Stream' /'* Channel'读/写操作的“低级”I/O方法时,它们最终都会以本地映射(FOR DEFAULT FILESYSTEM IMPLEMENTATIONS!)结束。这意味着(但请注意:这是我的猜测)在给出他们的实验后,他们发现最终'* Channel'给出了足够的性能优势,因此他们决定默认的'FileSystem'方法实现必须如此编写。但是对于一个非常完整,详细的答案,您可能想在OpenJDK的nio-dev列表中提出这个问题。 – fge