Buffered Streams in Java

Naveen AnamaneniUpdated · 5 min read

Buffered streams are wrapper streams that add a buffer, a block of memory, between your program and the underlying stream. Instead of going to the disk for every single byte or character, a buffered stream reads or writes a large block at once (8 KB by default) and serves your small requests from memory. This makes I/O much faster. The four classes are BufferedInputStream, BufferedOutputStream, BufferedReader and BufferedWriter. In this lesson, you will learn how to use them and measure the difference they make.

How Buffering Works

One trip to the disk fills the buffer; many small reads are then served from memory

The Buffered Classes

ClassWrapsExtra methods
BufferedInputStreamAny InputStreammark, reset
BufferedOutputStreamAny OutputStreamNone (just faster)
BufferedReaderAny ReaderreadLine(), lines()
BufferedWriterAny WriternewLine()

You create one by wrapping another stream: new BufferedReader(new FileReader("a.txt")).

Example: Reading Lines with BufferedReader

Let's see an example that writes and reads a file line by line.

Lines.java▶ Try it
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
 
public class Lines {
    public static void main(String[] args) throws IOException {
        try (BufferedWriter w = new BufferedWriter(
                new FileWriter("marks.csv", StandardCharsets.UTF_8))) {
            w.write("Navika,91");
            w.newLine();
            w.write("Srihitha,78");
            w.newLine();
            w.write("Advitha,85");
            w.newLine();
        }
 
        int total = 0;
        try (BufferedReader r = new BufferedReader(
                new FileReader("marks.csv", StandardCharsets.UTF_8))) {
            String line;
            while ((line = r.readLine()) != null) {
                String[] parts = line.split(",");
                System.out.println(parts[0] + " scored " + parts[1]);
                total += Integer.parseInt(parts[1]);
            }
        }
        System.out.println("Total: " + total);
    }
}
Output
Navika scored 91
Srihitha scored 78
Advitha scored 85
Total: 254

Explanation: BufferedWriter.newLine() writes the correct line ending for the operating system. BufferedReader.readLine() returns one line at a time without its line ending, and null at the end of the file. Both streams collect data in memory and touch the disk only in large blocks.

The Checkpoint: Measuring the Speed-Up

This program copies a 2 MB binary file twice, first one byte at a time with plain streams, then with buffered streams, and compares the times. Times depend on the computer, so the output is a sample.

CopySpeed.java▶ Try it
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
 
public class CopySpeed {
    static long copy(InputStream in, OutputStream out) throws IOException {
        long start = System.nanoTime();
        int b;
        while ((b = in.read()) != -1) {
            out.write(b);
        }
        return (System.nanoTime() - start) / 1_000_000;
    }
 
    public static void main(String[] args) throws IOException {
        byte[] data = new byte[2_000_000];
        new Random(1).nextBytes(data);
        try (OutputStream out = new FileOutputStream("photo.bin")) {
            out.write(data);
        }
 
        long plain;
        try (InputStream in = new FileInputStream("photo.bin");
             OutputStream out = new FileOutputStream("copy1.bin")) {
            plain = copy(in, out);
        }
 
        long buffered;
        try (InputStream in = new BufferedInputStream(
                 new FileInputStream("photo.bin"));
             OutputStream out = new BufferedOutputStream(
                 new FileOutputStream("copy2.bin"))) {
            buffered = copy(in, out);
        }
 
        System.out.println("Unbuffered: " + plain + " ms");
        System.out.println("Buffered:   " + buffered + " ms");
    }
}
Output
Unbuffered: 3787 ms
Buffered:   20 ms

Explanation: The same copy method runs twice. Without buffering, each read() and write() is a separate request to the operating system: 4 million system calls. With buffering, the streams move 8 KB at a time, so the same loop makes only about 500 system calls and finishes hundreds of times faster.

Common Mistakes

Forgetting to close or flush a BufferedWriter. The last block may stay in memory and never reach the file.

Wrapping a buffered stream in another buffered stream. It wastes memory without making anything faster.

Using Scanner for very large files. BufferedReader is faster for plain line reading (see "Scanner vs BufferedReader").

Interview Corner

Why are buffered streams faster?

Each read or write on an unbuffered file stream can become a separate system call, which is expensive. A buffered stream keeps an internal array (8 KB by default): reads fill the array with one large system call and then serve many small reads from memory, and writes collect data in the array and send it in one large call when it fills or is flushed. This greatly reduces the number of system calls, especially when the program reads or writes a byte, character or line at a time.

Practice

  1. Easy: Read a text file with BufferedReader and print only the lines that contain the word "Java".
  2. Harder: Change CopySpeed to also time a copy using in.transferTo(out) with plain file streams. Explain why it's fast without buffered wrappers.

Quick check: Buffered streams

4 questions
1What is the default buffer size of Java's buffered streams?
2Which class has readLine()?
3What can happen if you forget to close a BufferedWriter?
4Why does buffering speed up byte-by-byte copying?

Key Takeaways

Was this tutorial helpful?

Lesson 344 of 482