Buffer
Buffers in Java NIO are used to interact with NIO channels. As you know, data is read from the channel into the buffer, and written from the buffer to the channel.
A buffer is essentially a block of memory to which data can be written and then read from it. This piece of memory is wrapped as a NIO Buffer object and provides a set of methods for easy access to this piece of memory.
Basic usage of Buffer The
use of Buffer to read and write data generally follows the following four steps:
- Write data to Buffer
- call flip() method
- read data from buffer
- Call the clear() method or the compact() method
Once all the data has been read, the buffer needs to be emptied so that it can be written again. There are two ways to clear the buffer: call the clear() or compact() method. The clear() method will clear the entire buffer. The compact() method will only clear data that has already been read. Any unread data is moved to the beginning of the buffer, and newly written data is placed after the unread data in the buffer.
Here is an example using Buffer:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); //create buffer with capacity of 48 bytes ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); //read into buffer. while (bytesRead! = -1) { buf.flip(); //make buffer ready for read while(buf.hasRemaining()){ System.out.print((char) buf.get()); // read 1 byte at a time } buf.clear(); //make buffer ready for writing bytesRead = inChannel.read(buf); } aFile.close();Buffer's capacity, position and limit
A buffer is essentially a block of memory to which data can be written and then read from it. This piece of memory is wrapped as a NIO Buffer object and provides a set of methods for easy access to this piece of memory.
In order to understand how Buffer works, you need to be familiar with its three properties:
- capacity
- position
- limit
Here is a description of capacity, position and limit in read and write mode, the detailed explanation is after the illustration.
capacity
As a memory block, Buffer has a fixed size value, also called "capacity". You can only write capacity bytes, long, char and other types into it. Once the Buffer is full, it needs to be emptied (by reading data or clearing data) to continue writing data to it.
position
When you write data to the Buffer, position represents the current position. The initial position value is 0. When a byte, long and other data are written to the Buffer, the position will move forward to the next Buffer unit where data can be inserted. position can be up to capacity – 1.
When reading data, it is also reading from a specific location. When switching the Buffer from write mode to read mode, position will be reset to 0. When reading data from the Buffer's position, the position is moved forward to the next readable position.
limit
In write mode, the limit of the Buffer indicates how much data you can write to the Buffer at most. In write mode, limit is equal to the capacity of Buffer.
When switching Buffer to read mode, limit indicates how much data you can read at most. Therefore, when switching the buffer to read mode, limit will be set to the position value in write mode. In other words, you can read all the previously written data (the limit is set to the number of written data, which is the position in write mode)
Types of Buffer
Java NIO has the following Buffer types:
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
MappedByteBuffer is a bit special and will be covered in a dedicated chapter on it.
Buffer allocation
To obtain a Buffer object, you must first allocate it. Every Buffer class has an allocate method.
ByteBuffer buf = ByteBuffer.allocate(48);
CharBuffer buf = CharBuffer.allocate(1024);
Write
data to Buffer There are two ways to write data to Buffer:
- Write from Channel to Buffer.
- Write to Buffer through Buffer's put() method.
int bytesRead = inChannel.read(buf); //从channel写 buf.put(127); //Write by put method
There are many versions of the put method, allowing you to write data to the Buffer in different ways. For example, writing to a specified location, or writing an array of bytes to Buffer. Refer to the JavaDoc for more details on the Buffer implementation.
flip() method
The flip method switches the Buffer from write mode to read mode. Calling the flip() method will set the position back to 0 and set the limit to the previous position value.
In other words, position is now used to mark the read position, and limit indicates how many bytes, chars, etc. were written before - how many bytes, chars, etc. can be read now.
Reading data
from Buffer There are two ways to read data from Buffer:
- Read data from Buffer to Channel.
- Use the get() method to read data from the Buffer.
//read from buffer into channel. int bytesWritten = inChannel.write(buf); byte aByte = buf.get();There are many versions of the get method, allowing you to read data from the Buffer in different ways. For example, read from the specified position, or read data from the Buffer to a byte array. Refer to the JavaDoc for more details on the Buffer implementation.
The rewind() method
Buffer.rewind() sets the position back to 0, so you can reread all the data in the Buffer. The limit remains the same, still indicating how many elements (byte, char, etc.) can be read from the Buffer.
Once the clear() and compact() methods have
read the data in the Buffer, the Buffer needs to be ready to be written again. This can be done with the clear() or compact() methods.
If the clear() method is called, position will be set back to 0 and limit will be set to the value of capacity. In other words, the Buffer is emptied. The data in the buffer is not cleared, but these flags tell us where to start writing data to the buffer.
If there is some unread data in the Buffer, call the clear() method and the data will be "forgotten", meaning there will no longer be any markers that will tell you which data has been read and which has not.
If there is still unread data in the Buffer, and the data is needed later, but you want to write some data first, use the compact() method.
The compact() method copies all unread data to the beginning of the Buffer. Then set the position to just after the last unread element. The limit property is still set to capacity like the clear() method. The buffer is now ready to write data, but will not overwrite unread data.
mark() and reset() methods
A specific position in the Buffer can be marked by calling the Buffer.mark() method. This position can then be restored by calling the Buffer.reset() method. E.g:
buffer.mark(); //call buffer.get() a couple of times, e.g. during parsing. buffer.reset(); //set position back to mark.equals() and compareTo() methods
Two Buffers can be compared using the equals() and compareTo() methods.
equals()means that two Buffers are equal when the following conditions are met:
- have the same type (byte, char, int, etc.).
- The number of bytes, chars, etc. remaining in the Buffer is equal.
- All remaining bytes, chars, etc. in the Buffer are the same.
compareTo() method
compareTo() method compares the remaining elements (byte, char, etc.) of two Buffers. One Buffer is considered to be "less than" the other Buffer if the following conditions are met:
- The first unequal element is smaller than the corresponding element in the other Buffer.
- All elements are equal, but the first buffer is exhausted before the other (the first buffer has fewer elements than the other).