CLOVER🍀

That was when it all began.

NIOのByteBufferを使って、プリミティブ型とbyte配列の相互変換を行う

以前、こんなエントリを書きました。

Javaでbyte配列から、別のプリミティブ型に変換する
http://d.hatena.ne.jp/Kazuhira/20130512/1368368205

が、書き方がよくなかったみたい(?)で、NIOのByteBufferを使って変換しているところを見つけられていない方がいらっしゃったので、別にまとめることにしました。

すべて、NIOのByteBufferを使用します。

プリミティブ型→byte配列への変換

ここでは、long型からbyte配列への変換を行ってみます。longは8バイトなので、ByteBuffer#allocateで8バイト確保します。

ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.putLong(123456789L);
for (byte b : buffer.array()) {
    System.out.print(b & 0xff);
    System.out.print(" ");
}  // 0 0 0 0 7 91 205 21

あとは、ByteBuffer#putLongした後にByteBuffer#arrayを呼び出すと、byte配列表現となった値が取得できます。

ちなみに、嬉しいかどうかは別にしてputLongは自分自身を返すので、全部繋げて書くこともできます。

ByteBuffer.allocate(8).putLong(123456789L).array();

まあ、せめてallocateで切りましょうか…。

byte配列→プリミティブ型への変換

今度は、byte配列からlongへの変換を行ってみます。先ほどのlong→byte配列の結果を参考に、long値のbyte表現を用意します。

byte[] bytes = {0, 0, 0, 0, (byte) 7, (byte) 91, (byte) 205, (byte) 21};

あとは、ByteBuffer#wrapしてgetLongしてください。

System.out.println(ByteBuffer.wrap(bytes).getLong());  // => 123456789

ここでは、longを使いましたが、他にもchar、double、float、int、short、もちろんbyteもあります。booleanはありませんが。

ちなみに、このオペレーションで扱われる…というより、ByteBufferのバイトオーダーの初期値はビッグエンディアンです。

トルエンディアンで扱いたいのですが…

ではリトルエンディアンで扱う場合はどうするかというと、java.nio.ByteOrderを利用します。

先ほどのlong値のデータを、リトルエンディアンで用意します。

byte[] bytes = {(byte) 21, (byte) 205, (byte) 91, (byte) 7, 0, 0, 0, 0};

ByteBuffer#putLongした後で、ByteBuffer#orderでバイトオーダーをリトルエンディアンに変更します。

ByteBuffer buf = ByteBuffer.wrap(bytes);
buf.order(ByteOrder.LITTLE_ENDIAN);
System.out.println(buf.getLong());  // => 123456789

これで、無事リトルエンディアンのbyte配列もlongに戻せました。

嬉しいかどうかはさておき、こちらも1行で書けます。

ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong();

オマケ

特に単一の値にこだわらず、連続した値も普通に扱えるのでいろいろやってみましょう。

たとえば、intを3つ分確保して、byte配列に一気に変換。

ByteBuffer buf = ByteBuffer.allocate(12);  // int 3つ分
buf.putInt(12345);
buf.putInt(45678);
buf.putInt(78901);

for (byte b : buf.array()) {
    System.out.print(b & 0xff);
    System.out.print(" ");
}  // 0 0 48 57 0 0 178 110 0 1 52 53

得られたbyte値から、int3つに戻します。

byte[] binary = {0, 0, (byte) 48, (byte) 57,
                 0, 0, (byte) 178, (byte) 110,
                 0, (byte) 1, (byte) 52, (byte) 53};
ByteBuffer buffer = ByteBuffer.wrap(binary);
System.out.println(buffer.getInt());  // 12345
System.out.println(buffer.getInt());  // 45678
System.out.println(buffer.getInt());  // 78901

もしくは、こんな感じで表示?

byte[] binary = {0, 0, (byte) 48, (byte) 57,
                 0, 0, (byte) 178, (byte) 110,
                 0, (byte) 1, (byte) 52, (byte) 53};
ByteBuffer buffer = ByteBuffer.wrap(binary);
for (int i = buffer.position(); i < buffer.capacity() / 4; i++) {
    System.out.println(buffer.getInt());
}