Java BufferedReader

Jakob Jenkov
Last update: 2019-11-07

The Java BufferedReader class, java.io.BufferedReader, provides buffering for your Java Reader instances. Buffering can speed up IO quite a bit. Rather than read one character at a time from the underlying Reader, the Java BufferedReader reads a larger block (array) at a time. This is typically much faster, especially for disk access and larger data amounts.

The Java BufferedReader is similar to the BufferedInputStream but they are not exactly the same. The main difference between BufferedReader and BufferedInputStream is that BufferedReader reads characters (text), whereas the BufferedInputStream reads raw bytes.

The Java BufferedReader class is a subclass of the Java Reader class, so you can use a BufferedReader anywhere a Reader is required.

Java BufferedReader Example

To add buffering to a Java Reader instance, simply wrap it in a BufferedReader. Here is how that looks:

BufferedReader bufferedReader = new BufferedReader(
                      new FileReader("c:\\data\\input-file.txt"));

This example creates a BufferedReader which wraps a FileReader. The BufferedReader will read a block of characters from the FileReader (typically into a char array). Each character returned from read() is thus returned from this internal array. When the array is fully read the BufferedReader reads a new block of data into the array etc.

BufferedReader Buffer Size

You can set the buffer size to use internally by the BufferedReader. You provide the size as a constructor parameter, like this:

int bufferSize = 8 * 1024;

BufferedReader bufferedReader = new BufferedReader(
                      new FileReader("c:\\data\\input-file.txt"),
                      bufferSize
);

This example sets the internal buffer to 8 KB. It is best to use buffer sizes that are multiples of 1024 bytes. That works best with most built-in buffering in hard disks etc.

Except for adding buffering to Reader instances, a Java BufferedReader behaves pretty much like a Reader. The BufferedReader has one extra method though, the readLine() method. This method can be handy if you need to read input one line at a time. Here is a BufferedReader readLine() example:

String line = bufferedReader.readLine();

The readLine() method will return a textual line (all text until at line break is found) read from the BufferedReader. If there is no more data to read from the underlying Reader, then the BufferedReader's readLine() method will return null .

Read Characters From a BufferedReader

The read() method of a Java BufferedReader returns an int which contains the char value of the next character read. If the read() method returns -1, there is no more data to read in the BufferedReader, and it can be closed. That is, -1 as int value, not -1 as byte or char value. There is a difference here!

Here is an example of reading all characters from a Java BufferedReader:

Reader reader =
    new BufferedReader(
        new FileReader("/path/to/file/thefile.txt"));

int theCharNum = reader.read();
while(theCharNum != -1) {
    char theChar = (char) theCharNum;

    System.out.print(theChar);

    theCharNum = reader.read();
}

Notice how the code example first reads a single character from the Java BufferedReader and checks if the char numerical value is equal to -1. If not, it processes that char and continues reading until -1 is returned from the BufferedReader read() method.

As mentioned earlier, the BufferedReader will actually read an array of characters from the underlying Reader and return these characters one by one, rather than forwarding every read() call to the underlying Reader. When all characters in the internal buffer have been read, the BufferedReader attempts to refill the buffer again, until no more characters can be read from the underlying Reader.

Read Array of Characters From BufferedReader

The Java BufferedReader class also has a read() method that takes a char array as parameter, as well as a start offset and length. The char array is where the read() method will read the characters into. The offset parameter is where in the char array the read() method should start reading into. The length parameter is how many characters the read() method should read into the char array from the offset and forward. Here is an example of reading an array of characters into a char array with a Java BufferedReader:

Reader reader =
    new BufferedReader(
        new FileReader("/path/to/file/thefile.txt"));

char[] theChars = new char[128];

int charsRead = reader.read(theChars, 0, theChars.length);
while(charsRead != -1) {
    System.out.println(new String(theChars, 0, charsRead));
    charsRead = reader.read(theChars, 0, theChars.length);
}

The read(char[], offset, length) method returns the number of characters read into the char array, or -1 if there are no more characters to read in the BufferedReader, for instance if the end of the file the BufferedReader is connected to has been reached.

Read a Line From BufferedReader

The Java BufferedReader has a special read method named readLine() which reads a full line of text from the BufferedReader's internal buffer. The readLine() method returns a String. If there are no more lines to read from the BufferedReader, the readLine() method returns null. Here is an example of reading the lines of a text file one by one using a Java BufferedReader :

BufferedReader bufferedReader =
    new BufferedReader(
        new FileReader("/path/to/file/thefile.txt"));

String line = bufferedReader.readLine();
while(line != null) {
    System.out.println(line);
    line = bufferedReader.readLine();
}

Read Performance

Reading an array of characters at a time is faster than reading a single character at a time from a Java Reader. However, since the BufferedReader does some internal buffering already, the difference is most likely not as dramatic as with a Reader that uses no buffering. You will most likely still see a small difference though.

Skip Characters

The Java BufferedReader class has a method named skip() which can be used to skip over a number of characters in the input that you do not want to read. You pass the number of characters to skip as parameter to the skip() method. Here is an example of skipping characters from a Java BufferedReader :

long charsSkipped = bufferedReader.skip(24);

This example tells the Java BufferedReader to skip over the next 24 characters in the BufferedReader. The skip() method returns the actual number of characters skipped. In most cases that will be the same number as you requested skipped, but in case there are less characters left in the BufferedReader than the number you request skipped, the returned number of skipped characters can be less than the number of characters you requested skipped.

Closing a BufferedReader

When you are finished reading characters from the BufferedReader you should remember to close it. Closing a BufferedReader will also close the Reader instance from which the BufferedReader is reading.

Closing a BufferedReader is done by calling its close() method. Here is how closing a BufferedReader looks:

bufferedReader.close();

You can also use the try-with-resources construct introduced in Java 7. Here is how to use and close a BufferedReader looks with the try-with-resources construct:

Reader reader = new FileReader("data/data.bin");

try(BufferedReader bufferedReader =
    new BufferedReader(reader)){

    String line = bufferedReader.readLine();
    while(line != null) {
        //do something with line

        line = bufferedReader.readLine();
    }

}

Notice how there is no longer any explicit close() method call. The try-with-resources construct takes care of that.

Notice also that the first FileReader instance is not created inside the try-with-resources block. That means that the try-with-resources block will not automatically close this FileReader instance. However, when the BufferedReader is closed it will also close the Reader instance it reads from, so the FileReader instance will get closed when the BufferedReader is closed.

Reusable BufferedReader

One of the weaknesses of the standard Java BufferedReader is that it can only be used once. Once you close it, it's no longer usable. If you need to read a lot of files, or network streams, you have to create a new BufferedReader for each file or network stream you want to read. This means that you are creating a new object, and more importantly, a new char array which is used as buffer inside the BufferedReader. This can put pressure on the Java garbage collector, if the number of files or streams read is high, and if they are read quickly after each other.

An alternative is to create a reusable BufferedReader where you can replace the underlying source Reader, so the BufferedReader and its internal byte array buffer can be reused. To save you the trouble, I have created such a ReusableBufferedReader, and included the code for it further down this tutorial. First, I want to show you how using this ReusableBufferedReader looks.

Create a ReusableBufferedReader

First you need to create a ReusableBufferedReader. Here is an example of how to create a ReusableBufferedReader:

ReusableBufferedReader reusableBufferedReader =
    new ReusableBufferedReader(new char[1024 * 1024]);

This example creates a ReusableBufferedReader with a 2 MB char array (1024 * 1024 chars, 1 char = 2 bytes) as its internal buffer.

Set Source

When you have created a ReusableBufferedReader you need to set the Reader on it to use as underlying data source. Here is how you set a source Reader on a ReusableBufferedReader :

FileReader reader = new FileReader("/mydata/somefile.txt");

reusableBufferedReader.setSource(reader);

The setSource() method actually returns a reference to the ReusableBufferedReader, so you can actually create a ReusableBufferedReader and set the source in a single instruction:

ReusableBufferedReader reusableBufferedReader =
    new ReusableBufferedReader(new byte[1024 * 1024])
        .setSource(new FileReader("/mydata/somefile.txt"));

Reusing a ReusableBufferedReader

When you are done using the ReusableBufferedReader you need to close it. Closing it will only close the underlying source Reader. After closing a ReusableBufferedReader you can use it again, simply by setting a new source Reader on it. Here is how it looks to reuse a ReusableBufferedReader :

reusableBufferedReader.setSource(new FileReader("/mydata/file-1.txt"));

//read data from ReusableBufferedReader

reusableBufferedReader.close();


reusableBufferedReader.setSource(new FileReader("/mydata/file-1.txt"));

//read data from ReusableBufferedReader

reusableBufferedReader.close();

ReusableBufferedReader Code

Here is the code for the ReusableBufferedReader described above. Note, that this implementation only overrides the read() and read(char[] dest, int offset, int length) methods of the Reader class that it extends. The rest of the Reader methods have been left out to keep the code shorter - but you can implement them yourself in case you need them.

import java.io.IOException;
import java.io.Reader;

public class ReusableBufferedReader extends Reader {

    private char[]      buffer = null;
    private int         writeIndex = 0;
    private int         readIndex  = 0;
    private boolean     endOfReaderReached = false;

    private Reader      source = null;


    public ReusableBufferedReader(char[] buffer) {
        this.buffer = buffer;
    }

    public ReusableBufferedReader setSource(Reader source){
        this.source = source;
        this.writeIndex = 0;
        this.readIndex  = 0;
        this.endOfReaderReached = false;
        return this;
    }

    @Override
    public int read() throws IOException {
        if(endOfReaderReached) {
            return -1;
        }

        if(readIndex == writeIndex) {
            if(writeIndex == buffer.length) {
                this.writeIndex = 0;
                this.readIndex  = 0;
            }
            //data should be read into buffer.
            int bytesRead = readCharsIntoBuffer();
            while(bytesRead == 0) {
                //continue until you actually get some bytes !
                bytesRead = readCharsIntoBuffer();
            }

            //if no more data could be read in, return -1;
            if(bytesRead == -1) {
                return -1;
            }
        }

        return 65535 & this.buffer[readIndex++];
    }

    @Override
    public int read(char[] dest, int offset, int length) throws IOException {
        int charsRead = 0;
        int data = 0;
        while(data != -1 && charsRead < length){
            data = read();
            if(data == -1) {
                endOfReaderReached = true;
                if(charsRead == 0){
                    return -1;
                }
                return charsRead;
            }
            dest[offset + charsRead] = (char) (65535 & data);
            charsRead++;
        }
        return charsRead;
    }

    private int readCharsIntoBuffer() throws IOException {
        int charsRead = this.source.read(this.buffer, this.writeIndex, this.buffer.length - this.writeIndex);
        writeIndex += charsRead;
        return charsRead;
    }

    @Override
    public void close() throws IOException {
        this.source.close();
    }
}

Jakob Jenkov

Featured Videos

Java ConcurrentMap + ConcurrentHashMap

Java Generics

Java ForkJoinPool

P2P Networks Introduction

















Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next