3 Java NIO Buffer-翻譯

王金龍發表於2017-12-06

Buffer是用來跟Channel進行互動的。正如你所知道的,資料都是從Channel讀到Buffer中的,並從Buffer寫入到Channel中的。

Buffer本質上是一塊記憶體空間,你可以將資料寫入到這裡,並在之後讀取它。這個記憶體塊被包裝成NIO Buffer物件,並提供了一系列的方法來方便對這個記憶體塊資料的操作。

Buffer基本用法

使用Buffer來讀寫資料基本上按照以下幾個步驟來進行。

  1. 將資料寫入到Buffer
  2. 呼叫buffer.flip()方法。
  3. 從buffer中讀取資料。
  4. 呼叫buffer.clear()或buffer.compact()方法。

當你向Buffer中寫入資料時,buffer會跟蹤有多少資料已經寫入。當需要讀取資料時,你需要buffer.flip()方法將buffer從寫模式切換到讀模式。在讀模式中,你可以讀取所有已經寫入到Buffer中的資料。

當你已經讀完buffer中的資料,你需要清空buffer來保證buffer能夠再次被寫。清空buffer有兩種方法:clear()方法和compat()方法。clear()方法會清空所有的buffer中的資料,compat()方法只會清空已經讀過的資料,所有沒有讀過的資料會放到buffer的最前面,新寫入的資料會追加到未讀資料的後面。

這是一個Buffer使用的簡單例子,包括write,flip,read和clear方法。

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

Buffer本質上是一個可以進行讀寫操作的記憶體區域。這塊記憶體區域被包裝成Java Buffer物件,它包含了一系列的方法來簡化對記憶體資料塊的操作。

一個Buffer包含三個主要屬性必須要熟悉。

  • capacity
  • position
  • limit

position和limit所代表的意義跟Buffer的讀寫模式有關,capacity則跟讀寫模式無關。

下圖說明的是capacity,position和limit在讀寫模式中所代表的意義。

image

Capacity

作為一個記憶體塊,Buffer有固定的大小,叫做capacity。你最大隻能寫入capacity個bytes,longs,chars個資料到Buffer。一旦buffer滿了,你必須清空它(從buffer中讀取資料或呼叫clear方法)。

Position

當你向快取中寫入資料,position表示當前位置。初始的position為0,當一個byte,long等被寫入到buffer中時,poistion會移動到下一個可寫的資料單元。position的最大值為capacity-1。

從Buffer中讀取資料也是從指定的position開始。當使用flip()方法將buffer的模式從寫模式切換到讀模式,position位置變為0。資料從position指定的位置開始讀起,並移動到下一個可讀的單元。

Limit

在寫模式中,limit指定了有多少資料可以寫入到buffer中。在寫模式中,limit與capacity值相等。

當buffer切換到讀模式時,limit指定了buffer中可讀的資料的大小。因此,當buffer切換到讀模式時,limit被設定為寫模式時的position位置。換句話說,讀的資料可達到寫的資料一樣。

Buffer型別

Java NIO中包含以下幾種型別。

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

正如你所看到的,這些Buffer代表不同的資料型別。換句話說,可以通過char,short,int,long,float 或 double型別來操作緩衝區中的位元組。

分配Buffer

在獲得Buffer物件之前必須先分配。每一個Buffer類都有一個allcate()方法來實現。下面是一個ByteBuffer分配48個位元組容量的buffer的例子。

ByteBuffer buf = ByteBuffer.allcate(48);
複製程式碼

下面是一個CharBuffer分配1024個字元的例子:

CharBuffer buf = CharBuffer.allcate(1024);
複製程式碼

向Buffer中寫入資料

向Buffer中寫入資料有兩種方式: 1、通過Channel向Buffer中寫入資料。 2、通過buffer.put()方法寫入。 下面是一個通過Channel向Buffer中寫入資料的例子。

int byteRead = inChannel.read(buf); // read into buffer
複製程式碼

下面是一個通過Buffer的put()方法寫入資料的例子:

buf.put(127);
複製程式碼

put還有其他版本的方法。比方說,在指定的位置寫入資料,向buffer中寫入一個陣列。更多實現細節參考JavaDoc。

flip()

flip()將buffer從寫模式切換到讀模式。呼叫flip()方法將position重置為0,並將limit設定為原來position原來的位置。

換句話說,position表示當前讀的位置,limit表示目前可讀的資料。

從Buffer中讀取資料

從Buffer中讀取資料有兩種方式

  1. 將Buffer中的資料讀入到Channel。
  2. 通過Buffer的get()方法讀取。

下面是一個將資料從Buffer讀取到Channel中的例子。

// read from buffer into channel
int byteWritten = inChannel.write(buffer);
複製程式碼

下面是一個通過Buffer的get方法讀取資料的例子

byte aByte = buf.get();
複製程式碼

get方法還有很多其他的版本。例如,從指定的位置開始讀,從buffer中讀取一個陣列。更多實現細節參考JavaDoc。

rewind()方法

Buffer.rewind()方法將position重置為0,因此,可以從頭開始讀取Buffer中的資料。limit的值保持不變,仍然表示最多可讀取的資料量。

clear()和compat()方法

一旦完成從Buffer中讀取資料,需要讓Buffer準備好再次寫入,可以通過clear()方法和compat()方法來完成。

如果呼叫的是clear()方法,position會重置為0,並且limit重置為capacity的值。換句話說,Buffer被清空了。Buffer中的資料並沒有被清空,只是這些標記告訴我們從哪裡開始可以寫入資料。

當Buffer中還剩下一些未讀取的資料時呼叫了clear()方法,這些資料將會被忽略。這意味著不再有任何標記告訴我們哪些資料已經讀取過了,哪些資料還未讀取過。

如果Buffer中仍有未讀的資料,且後續還需要這些資料,但是此時想要先先寫些資料,那麼使用compact()方法。

compact()方法將所有未讀的資料拷貝到Buffer起始處。然後將position設到最後一個未讀元素正後面。limit屬性依然像clear()方法一樣,設定成capacity。現在Buffer準備好寫資料了,但是不會覆蓋未讀的資料。

mark方法與reset方法

通過呼叫Buffer.mark()方法可以標記一個指定的位置。通過Buffer的reset()方法可以回到原來mark時的position位置,下面是一個例子:

buffer.mark();
// call buffer.get() a couple of times. 
buffer.reset();
複製程式碼

equals方法和compareTo方法

可以通過equals方法和compareTo方法來比較兩個buffer。

equals方法

  1. 有相同的型別(byte、char、int等)。
  2. Buffer中剩餘的byte、char等的個數相等。
  3. Buffer中所有剩餘的byte、char等都相同。

正如你所見,equals僅比較了Buffer中的部分資料,而不是Buffer中的每一個元素。事實上,它僅僅比較了剩餘的元素。

compareTo方法

compareTo()方法比較兩個Buffer的剩餘元素(byte、char等), 如果滿足下列條件,則認為一個Buffer“小於”另一個Buffer:

  1. 第一個不相等的元素小於另一個Buffer中對應的元素 。
  2. 所有元素都相等,但第一個Buffer比另一個先耗盡(第一個Buffer的元素個數比另一個少)。

相關文章