java.nio.Buffer系の使い方を理解したい

java.nio.Buffer系がよく分からなかった。でも、色々調べたりしたら分かってきた。
今後のためのメモ。

バッファ使ううえで理解必要な概念。

  • 容量
  • リミット
  • ポジション(位置)
  • リマイニング

Java API ダイジェスト ByteBufferクラスの図を参考に理解した。

容量

文字通りそのバッファの容量。どの位、確保するか。
CharBufferを容量10文字で使おうと思ったら、まず以下のように宣言する必要がある。

CharBuffer cb = CharBuffer.allocate(10);

リミット

これがよく分からなくて、ハマった。
リミットは読み込み、書き込みできる要素の限界のこと。
例えば、以下のように容量を10文字確保してからリミットを5に設定すると5文字分しか使えない。
よって、6文字分をバッファに格納しようとするとエラーになる。容量は10文字分あるんだけどね。

CharBuffer cb = CharBuffer.allocate(10);
cb.limit(5);
cb.put("ABCDEF");  //error

また、容量とリミットの関係は以下の通り

容量 >= リミット

ポジション(位置)

バッファがどの位置にいるかを表す。
一文字読めば、一文字進むし。
一文字書けば、一文字進む。

CharBuffer cb = CharBuffer.allocate(10);
cb.position(); // 0 初期位置
cb.put("A");   // 1文字呼んで、1文字進む
cb.position(); // 1

ボジション(位置)はリミットを超えることができないので、

リミット >= ポジション(位置)

リマイニング

現在のポジション(位置)からリミットまでの要素数

CharBuffer cb = CharBuffer.allocate(10);
cb.remaining(); // 10
cb.put("A");   // 1文字呼んで、1文字進む
cb.remaining(); // 9

確認

java.nio.Buffer系の使い方が分からないので簡単なサンプルを作ってみた。 - ぐらめぬ・ぜぷつぇんのはてダ(2007 to 2011)を参考に、というかパクらせて頂いて、
確認のプログラムを書いてみた。

  1 import java.nio.CharBuffer;
  2 
  3 public class BufferUtil {
  4   public BufferUtil() {
  5   }
  6 
  7   public static void dump(CharBuffer b) {
  8     System.out.print("---> capacity[" +b.capacity() +"],");
  9     System.out.print("position[" +b.position() +"],");
 10     System.out.print("limit[" +b.limit() +"],");
 11     System.out.print("remaining[" +b.remaining() +"],");
 12     System.out.println("length[" +b.length() +"]");
 13   }
 14 
 15   public static void dump(String str, CharBuffer b) {
 16     System.out.println(str);
 17     System.out.print("--- >capacity[" +b.capacity() +"],");
 18     System.out.print("position[" +b.position() +"],");
 19     System.out.print("limit[" +b.limit() +"],");
 20     System.out.print("remaining[" +b.remaining() +"],");
 21     System.out.println("length[" +b.length() +"]");
 22   }
 23 
 24 }
  1 import java.nio.CharBuffer;
  2 import java.nio.BufferOverflowException;
  3 
  4 public class Hoge {
  5 
  6   public static void dumpCharBuffer(CharBuffer b) {
  7     char c = '\0';
  8 
  9     while (b.hasRemaining()) {
 10       c = b.get();
 11       if (c != 0) System.out.print("[" +c +"]: ");
 12       else        System.out.print("[ ]: ");
 13       BufferUtil.dump(b);
 14     }
 15   }
 16 
 17   public static void main(String[] args) {
 18     CharBuffer cb = CharBuffer.allocate(64);
 19     BufferUtil.dump("allocate(64)",cb);
 20     cb.limit(16);
 21     BufferUtil.dump("limit(16)",cb);
 22     cb.put("abcde".toCharArray());
 23     BufferUtil.dump("put(\"abcde\")",cb);
 24     cb.rewind();
 25     BufferUtil.dump("rewind()",cb);
 26     System.out.println("read buffer");
 27     dumpCharBuffer(cb);
 28     System.out.println("----------------");
 29     try {
 30       System.out.println("put(\"ABC\")");
 31       cb.put("ABC".toCharArray());
 32       BufferUtil.dump(cb);
 33     } catch (BufferOverflowException e) {
 34       System.out.println("Buffer Overflow!");
 35     }
 36     System.out.println("---------------");
 37     cb.position(5);
 38     cb.mark();
 39     BufferUtil.dump("position(5),mark()",cb);
 40     cb.put("DEF".toCharArray());
 41     BufferUtil.dump("put(\"DEF\")",cb);
 42     cb.reset();
 43     BufferUtil.dump("reset()",cb);
 44     dumpCharBuffer(cb);
 45     cb.clear();
 46     BufferUtil.dump("clear()",cb);
 47     cb.put("GHI".toCharArray());
 48     cb.flip();
 49     BufferUtil.dump("put(\"GHI\"),flip()",cb);
 50     System.out.println("read buffer");
 51     dumpCharBuffer(cb);
 52   }
 53 }

実行結果

~/Desktop 10763 $ java Hoge
allocate(64)
--- >capacity[64],position[0],limit[64],remaining[64],length[64]
limit(16)
--- >capacity[64],position[0],limit[16],remaining[16],length[16]
put("abcde")
--- >capacity[64],position[5],limit[16],remaining[11],length[11]
rewind()
--- >capacity[64],position[0],limit[16],remaining[16],length[16]
read buffer
[a]: ---> capacity[64],position[1],limit[16],remaining[15],length[15]
[b]: ---> capacity[64],position[2],limit[16],remaining[14],length[14]
[c]: ---> capacity[64],position[3],limit[16],remaining[13],length[13]
[d]: ---> capacity[64],position[4],limit[16],remaining[12],length[12]
[e]: ---> capacity[64],position[5],limit[16],remaining[11],length[11]
[ ]: ---> capacity[64],position[6],limit[16],remaining[10],length[10]
[ ]: ---> capacity[64],position[7],limit[16],remaining[9],length[9]
[ ]: ---> capacity[64],position[8],limit[16],remaining[8],length[8]
[ ]: ---> capacity[64],position[9],limit[16],remaining[7],length[7]
[ ]: ---> capacity[64],position[10],limit[16],remaining[6],length[6]
[ ]: ---> capacity[64],position[11],limit[16],remaining[5],length[5]
[ ]: ---> capacity[64],position[12],limit[16],remaining[4],length[4]
[ ]: ---> capacity[64],position[13],limit[16],remaining[3],length[3]
[ ]: ---> capacity[64],position[14],limit[16],remaining[2],length[2]
[ ]: ---> capacity[64],position[15],limit[16],remaining[1],length[1]
[ ]: ---> capacity[64],position[16],limit[16],remaining[0],length[0]
----------------
put("ABC")
Buffer Overflow!
---------------
position(5),mark()
--- >capacity[64],position[5],limit[16],remaining[11],length[11]
put("DEF")
--- >capacity[64],position[8],limit[16],remaining[8],length[8]
reset()
--- >capacity[64],position[5],limit[16],remaining[11],length[11]
[D]: ---> capacity[64],position[6],limit[16],remaining[10],length[10]
[E]: ---> capacity[64],position[7],limit[16],remaining[9],length[9]
[F]: ---> capacity[64],position[8],limit[16],remaining[8],length[8]
[ ]: ---> capacity[64],position[9],limit[16],remaining[7],length[7]
[ ]: ---> capacity[64],position[10],limit[16],remaining[6],length[6]
[ ]: ---> capacity[64],position[11],limit[16],remaining[5],length[5]
[ ]: ---> capacity[64],position[12],limit[16],remaining[4],length[4]
[ ]: ---> capacity[64],position[13],limit[16],remaining[3],length[3]
[ ]: ---> capacity[64],position[14],limit[16],remaining[2],length[2]
[ ]: ---> capacity[64],position[15],limit[16],remaining[1],length[1]
[ ]: ---> capacity[64],position[16],limit[16],remaining[0],length[0]
clear()
--- >capacity[64],position[0],limit[64],remaining[64],length[64]
put("GHI"),flip()
--- >capacity[64],position[0],limit[3],remaining[3],length[3]
read buffer
[G]: ---> capacity[64],position[1],limit[3],remaining[2],length[2]
[H]: ---> capacity[64],position[2],limit[3],remaining[1],length[1]
[I]: ---> capacity[64],position[3],limit[3],remaining[0],length[0]

簡単な解説

clear() flip() rewind()について

Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle

clear() は、新しい一連のチャネル読み込み操作または相対「put」操作のためにバッファを準備します。リミットを容量の値に設定し、位置を 0 に設定します。

flip() は、新規チャネル書き込みシーケンス (相対「get」) のためにバッファを準備します。リミットの値を現在位置の値に合わせたあと、位置の値を 0 にします。

rewind() は、すでにバッファ格納されているデータを再度読み込めるように、バッファを準備します。リミットの値はそのままで、位置の値を 0 にします。

ポジション(位置)を0にするのは共通で、リミットの値が異なります。
flip()の使いどころは、
java.nio.Buffer系の使い方が分からないので簡単なサンプルを作ってみた。 - ぐらめぬ・ぜぷつぇんのはてダ(2007 to 2011)

flip()というのは、Bufferに書き込まれた分だけちょうど取得したい時に呼んでおく、という使い方が出来るようです。

ということですね。

 37     cb.position(5);
 38     cb.mark();

ポジション(位置)5に移動して、そこの位置をマークしておく。
そうして、以下のようにリセットするとポジションがマークしておいた位置に戻る。

 42     cb.reset();

謝辞

java.nio.Buffer系の使い方を少しは理解できた。
それは、id:msakamoto-sfさんの
java.nio.Buffer系の使い方が分からないので簡単なサンプルを作ってみた。 - ぐらめぬ・ぜぷつぇんのはてダ(2007 to 2011)を読んだからです。
この記事がなかったら、理解できていなかっただろうと思います。
本当に、ありがとうございました。