Java: Используете наказание типов на примитивных массивах? - PullRequest
3 голосов
/ 13 февраля 2010

Мне нужно иметь возможность преобразовывать байтовые массивы в / из массивов других примитивных типов, но вместо приведения мне нужно type punning . Правильный термин для необработанной копии без приведения?

Я думал, что можно будет сделать следующее:

// 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

К сожалению, похоже, что bb.asIntBuffer() это , а не , создающий новый IntBuffer путем копирования содержимого «поразрядно» или «raw», но создающий новое «представление» существующего ByteBuffer. Вот почему .array() предназначен для отказа.

Я просмотрел исходники JDK и обнаружил несколько классов, которые используются всеми этими буферными классами, и будет делать то, что мне нужно, но является внутренним (например, класс Unsafe).

Хотя я думаю, что моя цель может быть достигнута, обернув буфер байтов в ObjectInputStream и прочитав примитивные значения .readInt(), я думаю, что это будет грязный и медленный обходной путь.

Итак, возможны ли другие решения без магической арифметики примитивного типа (сдвиг, проверка порядка, ...)?

ПРИМЕЧАНИЕ. Мне нужны оба направления: байт [12] -> int [3] и int [3] -> байт [12]

Ответы [ 2 ]

4 голосов
/ 13 февраля 2010

Согласно javadoc, array () [1] возвращает резервный массив буфера, который является массивом, который вы указываете при вызове wrap () [2].

Следовательно, вы должны создать новый массив с желаемым типом. Но арифметика все еще может быть обработана с помощью классов Buffer.

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);

В обратном направлении нужно немного вычислить самостоятельно.

ByteBuffer newBb = ByteBuffer.allocate(intArray.length*4);
newBb.asIntBuffer().put(intArray);
byte[] byteArray = newBb.array();

См:

[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

1 голос
/ 13 февраля 2010

Большое спасибо wierob за его код для преобразования байта [] -> int []!

Я немного поиграл, чтобы заставить работать противоположное направление.

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) Контрольный пример

{
    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) Выход

[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]
...