As explained in this answer, a buffered stream is supposed to reduce the number of system calls. This is only relevant, if the application makes a lot of small read or write requests, resulting in a lot of system calls. This is what the linked answer means by “inefficient”.
By using a significantly larger buffer which can be read or written with a single call and fulfilling application requests by copying from or to the buffer, you’re reducing the number of system calls. This improves the performance, if the saved system calls are more expensive than the introduced copying overhead.
So the reason why it is not always better to use a buffered stream, is that not every application is making such small requests. When an applications makes reasonably sized requests, the best, the buffered stream can do, is going out of the way, so if it has an empty buffer and the application makes a request of the same or even bigger size than the buffer’s size, the buffered stream will pass the request to the source stream directly.
But if the application’s buffer is only slightly less, the buffered stream will do its job of buffering, introducing additional copying overhead. But as said, you only gain an advantage, if you actually save system calls and, depending on the architecture, you might have to say, “…if you actually save a significant amount of system calls”. A larger buffer is not an improvement per se.
A simple example would be, say, you just want to write 1,000 bytes to a file, like
byte[] data = /* something producing an array of 1,000 bytes */
try (OutputStream os = Files.newOutputStream(path)) {
os.write(data);
}
So, if you wrap the output stream in a BufferedOutputStream
, you’ll get a buffer of the default size of 8192 bytes. Since this buffered stream has no knowledge of how much you are going to write totally, it will copy the request data to its buffer, as it is smaller, to be flushed (written) during the close operation. So in the end, you don’t save any system call, but get the copying overhead.
So a buffered stream is not always more efficient; the buffering may even degrade the performance in some cases. Also, sometimes an application is not interested in the highest performance, but in timely writing to the underlying media. It is easier to wrap an OutputStream
, to get buffering when needed, than to opt out the buffering, if the stream is already a BufferedOutputStream
.
When you look at the NIO Channel API, introduced in JDK 1.4, you’ll notice that there are no buffered channels. Instead, it doesn’t offer methods for read or writing a single byte, further, it forces the programmers to use a ByteBuffer
, to guide them to separate I/O and processing of the data. This is the preferred way to go.