Java: Using type punning on primitive arrays?
I need to be able to convert byte arrays to/from other primitive type arrays, but instead of casting, I need type punning. Correct term for raw copy without casting?
I thought it would be possible to do the following:
// idea: byte[12] -> int[3], and int[3] -> byte[12]
int[] ints;
ByteBuffer bb = ByteBuffer.wrap(
new byte[]{ 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3 });
IntBuffer ib = bb.asIntBuffer开发者_如何学编程();
ints = ib.array(); // java.lang.UnsupportedOperationException
ints = ib.duplicate().array(); // java.lang.UnsupportedOperationException
Unfortunately, it seems that bb.asIntBuffer()
is not creating a new IntBuffer by copying the content "bitwise" or "raw", but creates a new "view" on existing ByteBuffer. That's why .array()
is intended to fail.
I browsed around in JDK's sources, and found few classes, which are used by all these buffer classes and would do the stuff I need, but are internal (such as the class Unsafe
).
While I think that my goal could be achieved by wrapping the byte buffer in some ObjectInputStream
and read the primitive values by .readInt()
, I think it would be a messy and slow workaround.
So, are there any other solutions possible without doing magical primitive type arithmetics (shifting, checking endians, ...)?
NOTE: I need both directions: byte[12] -> int[3], and int[3] -> byte[12]
According to the javadoc, array() [1] returns the buffer's backing array which is the array you specify with the call to wrap() [2].
Hence, you must create a new array with the desired type. But the arithmetics can still be handled via the Buffer classes.
ByteBuffer bb = ByteBuffer.wrap(new byte[]{ 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3 });
IntBuffer ib = bb.asIntBuffer();
int[] intArray = new int[ib.limit()];
ib.get(intArray);
Backwards requires a little bit of calculation by yourself.
ByteBuffer newBb = ByteBuffer.allocate(intArray.length*4);
newBb.asIntBuffer().put(intArray);
byte[] byteArray = newBb.array();
See:
[1] http://java.sun.com/javase/6/docs/api/java/nio/ByteBuffer.html#array%28%29
[2] http://java.sun.com/javase/6/docs/api/java/nio/ByteBuffer.html#wrap%28byte[]%29
Many thanks to wierob for his code for converting byte[]->int[] !
I played around a bit to get the opposite direction working.
1) API
// byte[] -> int[]
public static int[] punnedToInteger(byte[] in){
ByteBuffer bb = ByteBuffer.wrap(in);
IntBuffer pb = bb.asIntBuffer();
int[] out = new int[pb.limit()];
pb.get(out);
return out;
}
// int[] -> byte[]
public static byte[] punnedFromInteger(int[] in){
byte[] out = new byte[in.length * Integer.SIZE / Byte.SIZE];
ByteBuffer bb = ByteBuffer.wrap(out);
for(int i=0; i<in.length; ++i){
bb.putInt(in[i]);
}
return out;
}
2) Test case
{
byte[] bytes = new byte[]{ 0,0,0,1, 0,0,1,0, 0,1,0,0, 1,0,0,0 };
int[] ints = punnedToInteger(bytes);
System.out.println(Arrays.toString(bytes));
System.out.println(Arrays.toString(ints));
System.out.println();
}
{
int[] ints = new int[]{ 1, 256, 65536, 16777216 };
byte[] bytes = punnedFromInteger(ints);
System.out.println(Arrays.toString(ints));
System.out.println(Arrays.toString(bytes));
System.out.println();
}
3) Output
[0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0]
[1, 256, 65536, 16777216]
[1, 256, 65536, 16777216]
[0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0]
精彩评论