java.nio.Bufferクラスは、便利なクラスなのですが、操作の方法が少々分かりにくいので、簡単にまとめてみました。サンプルはByteBufferを使用していますが、他のBufferでも基本的には同じです。
Bufferを新規に作成するには、allocate(int capacity)メソッドを使用します。下記のコードは、10バイトの容量を持つByteBufferを作成します。
ByteBuffer buffer = ByteBuffer.allocate(10);
Bufferには、いくつかの重要な値があります。capacity・limit・positionがそれです。以下のメソッドで、それぞれの値の取得や変更を行うことができます。
capacity()
Bufferの容量を返します。容量は、このBufferに格納できる最大のサイズです。ByteBufferの場合は、バイト数になります。容量は変更できません。
limit()/limit(int)
Bufferの制限値を返します。制限値は、このBufferに格納できる最大のサイズです。制限値は、容量の範囲内で変更可能です。
position()/position(int)
Bufferの現在の位置を返します。位置は、相対的な格納や取得で使用されます。位置は、リミットの範囲内で変更可能です。
先ほどのallocateメソッドでアロケートした直後のBufferは、capacity()が10を、limit()が10を、position()が0をそれぞれ返します。
| position() | |||||||||||||||
| | | limit() | ||||||||||||||
| | | capacity() | ||||||||||||||
| ↓ | ↓ | ||||||||||||||
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||
| ByteBuffer | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | |||||
相対的な格納動作は、Bufferのposition()が指す位置から、指定の値を格納します。position()は、格納した内容のバイト数分移動します。
byte[] bytes = "1234".getBytes(); buffer.put(bytes);
| position() | |||||||||||||||
| | | limit() | ||||||||||||||
| | | capacity() | ||||||||||||||
| ↓ | ↓ | ||||||||||||||
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||
| ByteBuffer | 0x31 | 0x32 | 0x33 | 0x34 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | |||||
buffer.putShort(54321);
| position() | |||||||||||||||
| | | limit() | ||||||||||||||
| | | capacity() | ||||||||||||||
| ↓ | ↓ | ||||||||||||||
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||
| ByteBuffer | 0x31 | 0x32 | 0x33 | 0x34 | 0xD4 | 0x31 | 0x00 | 0x00 | 0x00 | 0x00 | |||||
気をつけなければいけないのは、リミットを越えて(リミットの位置に)データを格納することができないということです。上の例で言えば、2バイトの数値を格納した後、5バイト以上のデータ(例えばlong)を格納しようとすると、java.nio.BufferUnderflowExceptionが発生します。
相対的な取得動作は、ByteBufferのposition()が指す位置から、指定のデータ型の値を取得します。
そのためには、当然位置を変更する必要があります。複数回のput()で編集したBufferから、一括でデータを取得したい場合に、便利なメソッドが容易されています。
flip()です。このメソッドは、リミットをポジションの位置に移動し、ポジションを先頭(0)に移動します。
| position() | |||||||||||||||
| | | limit() | ||||||||||||||
| | | capacity() | ||||||||||||||
| ↓ | ↓ | ||||||||||||||
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||
| ByteBuffer | 0x31 | 0x32 | 0x33 | 0x34 | 0xD4 | 0x31 | 0x00 | 0x00 | 0x00 | 0x00 | |||||
buffer.flip();
| position() | |||||||||||||||
| | | limit() | ||||||||||||||
| | | | | capacity() | |||||||||||||
| ↓ | ↓ | ↓ | |||||||||||||
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||
| ByteBuffer | 0x31 | 0x32 | 0x33 | 0x34 | 0xD4 | 0x31 | 0x00 | 0x00 | 0x00 | 0x00 | |||||
この状態で適切なサイズのbyte配列を作成し、get()を行うことで全てのデータを取得できます。
byte[] tmpBuf = new byte[buffer.limit()]; // 6バイト分の配列が作成される
buffer.get(tmpBuf);
| position() | |||||||||||||||
| limit() | |||||||||||||||
| | | capacity() | ||||||||||||||
| ↓ | ↓ | ||||||||||||||
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||
| ByteBuffer | 0x31 | 0x32 | 0x33 | 0x34 | 0xD4 | 0x31 | 0x00 | 0x00 | 0x00 | 0x00 | |||||
| tmpBuf | 0 | 1 | 2 | 3 | 4 | 5 | |||||||||
| 0x31 | 0x32 | 0x33 | 0x34 | 0xD4 | 0x31 | ||||||||||
相対的な操作と違い、絶対的な操作は、現在位置に関係なく、指定のインデックスが指す位置から、指定のデータ型の値を取得します。
この操作では、現在位置などの内容は変化しません。
| position() | |||||||||||||||
| limit() | |||||||||||||||
| | | capacity() | ||||||||||||||
| ↓ | ↓ | ||||||||||||||
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||
| ByteBuffer | 0x31 | 0x32 | 0x33 | 0x34 | 0xD4 | 0x31 | 0x00 | 0x00 | 0x00 | 0x00 | |||||
buffer.getShort(4); // 54321(0xD431)が返ります