我是否应该总是将InputStream包装成BufferedInputStream?

45

当我知道给定的InputStream不是缓冲的时,总是将InputStream包装为BufferedInputStream是否有意义?例如:

InputStream is = API.getFromSomewhere()
if(!(is instanceof BufferedInputStream))
  return new BufferedInputStream(is);
return is;
4个回答

38
在我知道给定的InputStream不是缓冲的情况下,总是将InputStream包装成BufferedInputStream是否有意义? 不是。 如果您可能会执行大量小读取(一次一个字节或几个字节),或者想使用缓冲API提供的某些更高级功能,则有意义;例如,BufferedReader.readLine()方法。 然而,如果您只打算使用read(byte[])和/或read(byte[], int, int)方法执行大块读取,则将InputStream包装在BufferedInputStream中并没有帮助。 (针对@Peter Tillman自己回答的评论,块读取用例绝对代表了InputStream类使用的0.1%以上!!但是,他在这样做时通常是无害的。)

7
我不会这样做,我会尽可能地保留最高的抽象级别。如果您不打算使用BufferedStream的标记和重置功能,为什么要包装它呢?
如果消费者需要它,最好在那里进行包装。

12
这似乎意味着BufferedInputStream相较于普通的InputStream,只添加了标记和重置两个有用的功能。从API的角度来看这可能是正确的,但正如其他人所说,BufferedInputStream为您处理读取缓冲。从裸露的FileInputStream逐字节读取比从一个包装在BufferedInputStream中的流读取要慢40倍。话虽如此,返回InputStream并保持方法签名不变。用户可以选择包装它们。 - jasonmp85
我同意从性能角度来看,在99.9%的情况下将其包装起来更好。然而,这确实使消费者免除了思考如何使用InputStream的责任。消费者的这种假设限制了可重用性。 - Peter Tillemans
我认为在超过0.1%的情况下,消费者不会逐字节地阅读,而是会使用某种缓冲区,这时BufferedInputStream就成了无用的开销。 - Michael Borgwardt
@Michael,我认为Peter的观点是,99%的情况下它比逐字节读取要快,而不是99%的时间用作逐字节读取。 - Yishai

3
您并不总是需要缓冲区,因此,在这种情况下,答案是否定的,有时它只会增加开销。
还有另一个原因,就是当您在网络套接字上启用超时时,使用BufferedInputStream(或BufferedReader)可能会导致不可预测的失败。超时可能发生在读取数据包时。即使您知道有一些非零字节数(请参见java.net.SocketTimeoutException,它是java.io.InterruptedIOException的子类,因此具有bytesTransferred变量),您也将无法再访问已传输到该点的数据。
如果您想知道在读取时如何发生套接字超时,请考虑调用read(bytes[])方法,并且包含消息的原始数据包被拆分,但其中一个部分包被延迟超过超时时间(或剩余部分的超时时间)。当再次封装在实现java.io.DataInput的内容中时,这种情况可能更频繁发生(例如,多个字节值的任何读取,如readLong()或readFully()或BufferedReader.readLine()方法)。
请注意,java.io.DataInputStream对于具有超时的套接字流也不是一个好的选择,因为它对超时异常的处理也不好。

1
关于BufferedInputStream和BufferedReader,这是一个谣言。如果你遇到了读取超时的情况,(i)你正在读取,因此内部缓冲区为空,否则你就不会读取;(ii)在超时期间没有数据到达。因此,没有数据丢失。试一下。 - user207421
@EJP:你让我真的开始思考这个问题,但我仍然认为这可能是一个问题。当缓冲流真正需要进行I/O(填充缓冲区)时,这就是您可能会遇到超时异常并且内部变量无法跟踪缓冲区中有多少字节未更新的点。我已经尝试过测试,虽然我可以复制超时异常,但似乎还不能复制bytesTransferred非零的情况。在那之前,我无法证明这一点。[我曾经在DataInputStream和超时中丢失过数据。] - Kevin Brock
@EJP:也许从套接字中读取字节数组由于超时而导致部分读取缓冲区的情况从未发生过,因此bytesTransferred从未为非零值(否则BufferredInputStream将失败)。这也可能是不同平台/供应商上JVM实现可能产生不同结果的情况 - 我刚在带有Sun / Oracle Java的Windows 7上进行了测试。 - Kevin Brock
1
但是BufferedInputStream不会“填充缓冲区”。请参阅Javadoc。它读取任何可以读取的内容,就像其他读取一样,并返回该长度。具体而言,它永远不会两次阻塞。如果在超时期间到达任何数据,则没有超时。相反,如果有超时,则没有数据到达。因此,没有任何东西会丢失。对于DataInputStream,问题是真实存在的。对于BufferedInputStream,不是这样的。 - user207421

1

这也取决于您将如何从InputStream中读取。如果您打算逐个字符/字节地读取它(即read()),那么BufferedInputStream将通过代表您进行批量读取来减少开销。如果您打算一次性将其读入4k或8k字节/字符数组中的一个块中,则BuffredInputStream可能不会对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接