Java NIO Buffer
- Basic Buffer Usage
- Buffer Capacity, Position and Limit
- Buffer Types
- Allocating a Buffer
- Writing Data to a Buffer
- flip()
- Reading Data from a Buffer
- rewind()
- clear() and compact()
- mark() and reset()
- equals() and compareTo()
Java NIO Buffers are used when interacting with NIO Channels. As you know, data is read from channels into buffers, and written from buffers into channels.
A buffer is essentially a block of memory into which you can write data, which you can then later read again. This memory block is wrapped in a NIO Buffer object, which provides a set of methods that makes it easier to work with the memory block.
Buffer 一般称为缓冲区,实际上是一块内存区域---被封装成了NIO Buffer对象,并提供了一系列的方法,用于写+后续的读。
Basic Buffer Usage
Using a Buffer
to read and write data typically follows this little 4-step process:
- Write data into the Buffer
- Call
buffer.flip()
- Read data out of the Buffer
- Call
buffer.clear()
orbuffer.compact()
When you write data into a buffer, the buffer keeps track of how much data you have written. Once you need to read the data, you need to switch the buffer from writing mode into reading mode using the flip()
method call. In reading mode the buffer lets you read all the data written into the buffer.
Once you have read all the data, you need to clear the buffer, to make it ready for writing again. You can do this in two ways: By calling clear()
or by calling compact()
. The clear()
method clears the whole buffer. The compact()
method only clears the data which you have already read. Any unread data is moved to the beginning of the buffer, and data will now be written into the buffer after the unread data.
读取完所有的数据后,需要clear掉buffer中数据才可以继续写入。有以下两种方式:
clear:清除掉所有buffer中的数据。
compact:仅清除掉已读取的数据,未读数据会移动到buffer的开始端。
Here is a simple Buffer
usage example, with the write, flip, read and clear operations maked in bold:
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 Capacity, Position and Limit
A buffer is essentially a block of memory into which you can write data, which you can then later read again. This memory block is wrapped in a NIO Buffer object, which provides a set of methods that makes it easier to work with the memory block.
A Buffer
has three properties you need to be familiar with, in order to understand how a Buffer
works. These are:
- capacity
- position
- limit
The meaning of position
and limit
depends on whether the Buffer
is in read or write mode. Capacity always means the same, no matter the buffer mode.
在读、写场景,position和limit的含义不同,但是capacity始终代表buffer的总容量
Here is an illustration(插图) of capacity, position and limit in write and read modes. The explanation follows in the sections after the illustration.
buffers-modes.png Buffer capacity, position and limit in write and read mode.
Capacity
Being a memory block, a Buffer
has a certain fixed size, also called its "capacity". You can only write capacity
bytes, longs, chars etc. into the Buffer. Once the Buffer is full, you need to empty it (read the data, or clear it) before you can write more data into it.
固定大小的buffer size,就是capacity,
Position
When you write data into the Buffer
, you do so at a certain position. Initially the position is 0. When a byte, long etc. has been written into the Buffer
the position is advanced to point to the next cell in the buffer to insert data into. Position can maximally become capacity - 1
.
当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.
When you read data from a Buffer
you also do so from a given position. When you flip a Buffer
from writing mode to reading mode, the position is reset back to 0. As you read data from the Buffer
you do so from position
, and position
is advanced to next position to read.
当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
Limit
In write mode the limit of a Buffer
is the limit of how much data you can write into the buffer. In write mode the limit is equal to the capacity of the Buffer
.
写模式下,limit表示可以往buffer中写入的数据大小,等于capacity
When flipping the Buffer
into read mode, limit means the limit of how much data you can read from the data. Therefore, when flipping a Buffer
into read mode, limit is set to write position of the write mode. In other words, you can read as many bytes as were written (limit is set to the number of bytes written, which is marked by position).
当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
Buffer Types
Java NIO comes with the following Buffer types:
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
As you can see, these Buffer
types represent different data types. In other words, they let you work with the bytes in the buffer as char, short, int, long, float or double instead.
The MappedByteBuffer
is a bit special, and will be covered in its own text.
Allocating a Buffer
To obtain a Buffer
object you must first allocate it.
Every Buffer
class has an allocate()
method that does this. Here is an example showing the allocation of a ByteBuffer
, with a capacity of 48 bytes:
ByteBuffer buf = ByteBuffer.allocate(48);
Here is an example allocating a CharBuffer
with space for 1024 characters:
CharBuffer buf = CharBuffer.allocate(1024);
Writing Data to a Buffer
You can write data into a Buffer
in two ways:
- Write data from a
Channel
into aBuffer
- Write data into the
Buffer
yourself, via the buffer'sput()
methods.
Here is an example showing how a Channel
can write data into a Buffer
:
int bytesRead = inChannel.read(buf); //read into buffer from channel.
Here is an example that writes data into a Buffer
via the put()
method:
buf.put(127);
There are many other versions of the put()
method, allowing you to write data into the Buffer
in many different ways. For instance, writing at specific positions, or writing an array of bytes into the buffer. See the JavaDoc for the concrete buffer implementation for more details.
flip()
The flip()
method switches a Buffer
from writing mode to reading mode. Calling flip()
sets the position
back to 0, and sets the limit
to where position just was.
In other words, position
now marks the reading position, and limit
marks how many bytes, chars etc. were written into the buffer - the limit of how many bytes, chars etc. that can be read.
Reading Data from a Buffer
There are two ways you can read data from a Buffer
.
- Read data from the buffer into a channel.
- Read data from the buffer yourself, using one of the get() methods.
Here is an example of how you can read data from a buffer into a channel:
//read from buffer into channel.
int bytesWritten = inChannel.write(buf);
Here is an example that reads data from a Buffer
using the get() method:
byte aByte = buf.get();
There are many other versions of the get()
method, allowing you to read data from the Buffer
in many different ways. For instance, reading at specific positions, or reading an array of bytes from the buffer. See the JavaDoc for the concrete buffer implementation for more details.
rewind()
The Buffer.rewind()
sets the position
back to 0, so you can reread all the data in the buffer. The limit
remains untouched, thus still marking how many elements (bytes, chars etc.) that can be read from the Buffer
.
将当前读取位置设置为0,position=0,意味着所有数据重新置为未读状态
不影响limit字段数据
clear() and compact()
Once you are done reading data out of the Buffer
you have to make the Buffer
ready for writing again. You can do so either by calling clear()
or by calling compact()
.
If you call clear()
the position
is set back to 0 and the limit
to capacity
. In other words, the Buffer
is cleared. The data in the Buffer
is not cleared. Only the markers telling where you can write data into the Buffer
are.
注意,调用clear并不会清除buffer中的数据,而只是告诉代码,从position-0开始可以重新插入数据,覆盖掉原来的。
If there is any unread data in the Buffer
when you call clear()
that data will be "forgotten", meaning you no longer have any markers telling what data has been read, and what has not been read.
If there is still unread data in the Buffer
, and you want to read it later, but you need to do some writing first, call compact()
instead of clear()
.
compact()
copies all unread data to the beginning of the Buffer
. Then it sets position
to right after the last unread element. The limit
property is still set to capacity
, just like clear()
does. Now the Buffer
is ready for writing, but you will not overwrite the unread data.
mark() and reset()
You can mark a given position in a Buffer
by calling the Buffer.mark()
method. You can then later reset the position back to the marked position by calling the Buffer.reset()
method. Here is an example:
buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing.
buffer.reset(); //set position back to mark.
通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。
之后可以通过调用Buffer.reset()方法恢复到这个position。例如:
equals() and compareTo()
It is possible to compare two buffers using equals()
and compareTo()
.
equals()
Two buffers are equal if:
- They are of the same type (byte, char, int etc.)
- They have the same amount of remaining bytes, chars etc. in the buffer.
- All remaining bytes, chars etc. are equal.
As you can see, equals only compares part of the Buffer
, not every single element inside it. In fact, it just compares the remaining elements in the Buffer
.
如你所见,equals只是比较Buffer的一部分,不是每一个在它里面的元素都比较。实际上,它只比较Buffer中的剩余元素。
compareTo()
The compareTo()
method compares the remaining elements (bytes, chars etc.) of the two buffers, for use in e.g. sorting routines. A buffer is considered "smaller" than another buffer if:
- The first element which is equal to the corresponding element in the other buffer, is smaller than that in the other buffer.
- All elements are equal, but the first buffer runs out of elements before the second buffer does (it has fewer elements).
compareTo()方法比较两个Buffer的剩余元素(byte、char等), 如果满足下列条件,则认为一个Buffer“小于”另一个Buffer:
- 第一个不相等的元素小于另一个Buffer中对应的元素 。
- 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。
网友评论