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)が返ります