04. Java NIO Buffer 緩衝區

Java NIO Buffers用於和NIO Channel交互。正如你已經知道的,咱們從channel中讀取數據到buffers裏,從buffer把數據寫入到channels.app

buffer本質上就是一塊內存區,能夠用來寫入數據,並在稍後讀取出來。這塊內存被NIO Buffer包裹起來,對外提供一系列的讀寫方便開發的接口。dom

Buffer基本用法(Basic Buffer Usage)

利用Buffer讀寫數據,一般遵循四個步驟:大數據

  • 把數據寫入buffer;
  • 調用flip;
  • 從Buffer中讀取數據;
  • 調用buffer.clear()或者buffer.compact()

當寫入數據到buffer中時,buffer會記錄已經寫入的數據大小。當須要讀數據時,經過flip()方法把buffer從寫模式調整爲讀模式;在讀模式下,能夠讀取全部已經寫入的數據。spa

當讀取完數據後,須要清空buffer,以知足後續寫入操做。清空buffer有兩種方式:調用clear()或compact()方法。clear會清空整個buffer,compact則只清空已讀取的數據,未被讀取的數據會被移動到buffer的開始位置,寫入位置則近跟着未讀數據以後。rest

這裏有一個簡單的buffer案例,包括了write,flip和clear操做:code

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的容量,位置,上限(Buffer Capacity, Position and Limit)

buffer緩衝區實質上就是一塊內存,用於寫入數據,也供後續再次讀取數據。這塊內存被NIO Buffer管理,並提供一系列的方法用於更簡單的操做這塊內存。對象

一個Buffer有三個屬性是必須掌握的,分別是:排序

  • capacity容量
  • position位置
  • limit限制

position和limit的具體含義取決於當前buffer的模式。capacity在兩種模式下都表示容量。接口

下面有張示例圖,描訴了不一樣模式下position和limit的含義:ip

buffers-modes.png

Buffer capacity, position and limit in write and read mode.

容量(Capacity)

做爲一塊內存,buffer有一個固定的大小,叫作capacity容量。也就是最多隻能寫入容量值得字節,整形等數據。一旦buffer寫滿了就須要清空已讀數據以便下次繼續寫入新的數據。

位置(Position)

當寫入數據到Buffer的時候須要中一個肯定的位置開始,默認初始化時這個位置position爲0,一旦寫入了數據好比一個字節,整形數據,那麼position的值就會指向數據以後的一個單元,position最大能夠到capacity-1.

當從Buffer讀取數據時,也須要從一個肯定的位置開始。buffer從寫入模式變爲讀取模式時,position會歸零,每次讀取後,position向後移動。

上限(Limit)

在寫模式,limit的含義是咱們所能寫入的最大數據量。它等同於buffer的容量。

一旦切換到讀模式,limit則表明咱們所能讀取的最大數據量,他的值等同於寫模式下position的位置。

數據讀取的上限時buffer中已有的數據,也就是limit的位置(原position所指的位置)。

Buffer Types

Java NIO有以下具體的Buffer類型:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

正如你看到的,Buffer的類型表明了不一樣數據類型,換句話說,Buffer中的數據能夠是上述的基本類型;

MappedByteBuffer稍有不一樣,咱們會單獨介紹。

分配一個Buffer(Allocating a Buffer)

爲了獲取一個Buffer對象,你必須先分配。每一個Buffer實現類都有一個allocate()方法用於分配內存。下面看一個實例,開闢一個48字節大小的buffer:

ByteBuffer buf = ByteBuffer.allocate(48);

開闢一個1024個字符的CharBuffer:

CharBuffer buf = CharBuffer.allocate(1024);

寫入數據到Buffer(Writing Data to a Buffer)

寫數據到Buffer有兩種方法:

  • 從Channel中寫數據到Buffer
  • 手動寫數據到Buffer,調用put方法

下面是一個實例,演示從Channel寫數據到Buffer:

int bytesRead = inChannel.read(buf); //read into buffer.

經過put寫數據:

buf.put(127);

put方法有不少不一樣版本,對應不一樣的寫數據方法。例如把數據寫到特定的位置,或者把一個字節數據寫入buffer。看考JavaDoc文檔能夠查閱的更多數據。

翻轉(flip())

flip()方法能夠吧Buffer從寫模式切換到讀模式。調用flip方法會把position歸零,並設置limit爲以前的position的值。 也就是說,如今position表明的是讀取位置,limit標示的是已寫入的數據位置。

從Buffer讀取數據(Reading Data from a Buffer)

衝Buffer讀數據也有兩種方式。

  • 從buffer讀數據到channel
  • 從buffer直接讀取數據,調用get方法

讀取數據到channel的例子:

//read from buffer into channel.
int bytesWritten = inChannel.write(buf);

調用get讀取數據的例子:

byte aByte = buf.get();

get也有諸多版本,對應了不一樣的讀取方式。

rewind()

Buffer.rewind()方法將position置爲0,這樣咱們能夠重複讀取buffer中的數據。limit保持不變。

clear() and compact()

一旦咱們從buffer中讀取完數據,須要複用buffer爲下次寫數據作準備。只須要調用clear或compact方法。

clear方法會重置position爲0,limit爲capacity,也就是整個Buffer清空。實際上Buffer中數據並無清空,咱們只是把標記爲修改了。

若是Buffer還有一些數據沒有讀取完,調用clear就會致使這部分數據被「遺忘」,由於咱們沒有標記這部分數據未讀。

針對這種狀況,若是須要保留未讀數據,那麼可使用compact。 所以compact和clear的區別就在於對未讀數據的處理,是保留這部分數據仍是一塊兒清空。

mark() and reset()

經過mark方法能夠標記當前的position,經過reset來恢復mark的位置,這個很是像canva的save和restore:

buffer.mark();

//call buffer.get() a couple of times, e.g. during parsing.

buffer.reset();  //set position back to mark.

equals() and compareTo()

能夠用eqauls和compareTo比較兩個buffer

equals()

判斷兩個buffer相對,需知足:

  • 類型相同
  • buffer中剩餘字節數相同
  • 全部剩餘字節相等

從上面的三個條件能夠看出,equals只比較buffer中的部份內容,並不會去比較每個元素。

compareTo()

compareTo也是比較buffer中的剩餘元素,只不過這個方法適用於比較排序的:

相關文章
相關標籤/搜索