Introduction to buffers
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.
In order to understand how Buffer works, you need to be familiar with its three properties:
- capacity
- position
- limit
The meaning of position and limit depends on whether the Buffer is in read mode or write mode. No matter what mode the Buffer is in, the meaning of capacity is always the same.
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, the position is reset to 0. When reading data from the Buffer's position, the position moves 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)
Buffer method
flip(): Switch the Buffer read mode to write mode, set the position to 0, and set the limit to the value of the previous position.
clear(): Clear the entire buffer, position will be set back to 0, limit will be set to the value of capacity. The data in the Buffer is not cleared. If there is some unread data in the Buffer, call the clear() method, the data will be "Forgotten" means that there are no longer any markers that will tell you which data has been read and which has not.
compact(): Only data that has been read will be cleared. Any unread data is moved to the beginning of the buffer, newly written data is placed after the unread data in the buffer, and the position is set 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.
allocate(1024): Initialize the Buffer, and the set value determines the size of the capacity value
rewind(): Set 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
mark() and reset(): By calling the Buffer.mark() method, you can mark a specific position in the Buffer. You can then restore to this position by calling the Buffer.reset() method
equals(): Two Buffers are equal when the following three conditions are met
- have the same type (byte, char, int, etc.)
- The number of bytes, chars, etc. remaining in the Buffer is equal
- Buffer中所有剩余的byte、char等都相同
只比较的是剩余的数据
compareTo():满足下列条件,则认为一个Buffer“小于”另一个Buffer
- 第一个不相等的元素小于另一个Buffer中对应的元素
- 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)
注意点:
-
ByteBuffer byteBuffer = ByteBuffer.allocate(1);
在从通道往buffer
中读入之后,使用byteBuffer.get()
获取的时候,不可重复调用,因为get()
方法会移动position
,使得多次调用get()
方法获取的内容是不同的 -
在
ByteBuffer
中,put(int index, byte b)
方法不会移动position
,但是put(byte b)
会移动position
-
ByteBuffer.allocate(int capacity)
和ByteBuffer.allocateDirect(int capacity)
的区别:使用allocate
来创建缓冲区,并不是一下子就分配给缓冲区capacity
大小的空间,而是根据缓冲区中存储数据的情况来动态分配缓冲区的大小(实际上,在底层Java采用了数据结构中的堆来管理缓冲区的大小),因此,这个capacity
可以是一个很大的值,如1024*1024(1M)
。使用allocateDirect(由操作系统分配,脱离了JVM)
方法可以一次性分配capacity
大小的连续字节空间。通过allocateDirect
方法来创建具有连续空间的ByteBuffer
对象虽然可以在一定程度上提高效率,但这种方式并不是平台独立的。也就是说,在一些操作系统平台上使用allocateDirect
方法来创建ByteBuffer
对象会使效率大幅度提高,而在另一些操作系统平台上,性能会表现得非常差。而且allocateDirect
方法需要较长的时间来分配内存空间,在释放空间时也较慢。因此,在使用allocateDirect
方法时应谨慎
参考链接:
http://www.jianshu.com/p/052035037297