Java преобразовывает byte [] в int [] путем конкатенации каждые два байта - PullRequest
0 голосов
/ 25 декабря 2018

У меня есть массив byte[ ].Как я могу объединить каждые 2 байта (получить 16-битное короткое размерное значение для всех) и преобразовать в int ?

Я конвертирую, используя цикл for, номой байтовый массив длинный, поэтому цикл for делает его медленным.

Например:

байтовый массив имеет вид: {0x00, 0x01, 0x01, 0x02, 0x03, 0x04}

Iхочу конкатенировать каждые 2 байта следующим образом: {0x0001, 0x0102, 0x0304}

Затем получить массив int [] , например: { 1, 258, 772 } и т. д. *

IМожно сказать в двух словах: получить короткое (16-битное) значение путем объединения двух байтов, а затем привести его к типу int.

Вот как я сейчас выдал, но это медленно:

byte[] buffer; // This is my byte array
int[] intBuffer = new int[buffer.length / 2];

for(int i = 0; i < buffer.length-1; i+=2){
    intBuffer[i/2] = ((buffer[i] << 8) | buffer[i+1]);
}

Можно ли ускорить этот процесс с помощью библиотек Java?

Спасибо.

1 Ответ

0 голосов
/ 26 декабря 2018

Использование ByteBuffer для преобразования двух байтов за раз.

public static int[] twoBytesToInts(byte[] bytes) {
    ShortBuffer buffer = ByteBuffer.wrap(bytes).asShortBuffer();
    int[] ints = new int[buffer.remaining()];
    for (int i = 0; i < ints.length; i++)
        ints[i] = buffer.get(i) & 0xFFFF;
    return ints;
}

При этом используется Unsafe для чтения двух байтов за раз, чтобы избежать сдвигов и т. Д.

Использование Unsafe напрямую, чтобы избежатьСоздавая объекты, вы можете делать следующее:

public static Unsafe getUnsafe() {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        return (Unsafe) theUnsafe.get(null);
    } catch (NoSuchFieldException | IllegalAccessException e) {
        throw new AssertionError(e);
    }
}

public static int[] unsafeTwoBytesToInts(byte[] bytes) {
    Unsafe unsafe = getUnsafe();
    int[] ints = new int[bytes.length / 2];
    for (int i = 0; i < ints.length; i++)
        ints[i] = Short.reverseBytes(
                unsafe.getShort(bytes, i * 2 + Unsafe.ARRAY_BYTE_BASE_OFFSET)) & 0xFFFF;
    return ints;
}

Запуск этого с

public static void main(String... args) {
    byte[] bytes = {0x00, 0x01, 0x01, 0x02, 0x03, 0x04};
    int[] ints = twoBytesToInts(bytes);
    System.out.println(Arrays.toString(ints));
    int[] ints2 = unsafeTwoBytesToInts(bytes);
    System.out.println(Arrays.toString(ints2));
}

отпечатками

[1, 258, 772]
[1, 258, 772]

ПРИМЕЧАНИЕ. Если вы прочитали код для Short.reverseBytesпохоже, что на x86 происходит смещение, JIT заменяет этот код встроенной инструкцией машинного кода, чтобы сделать то же самое.

public static int[] twoBytesToIntsOriginal(byte[] bytes) {
    int[] intBuffer = new int[bytes.length / 2];

    for (int i = 0; i < bytes.length - 1; i += 2) {
        intBuffer[i / 2] = ((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF);
    }
    return intBuffer;
}

Выполнение их через тест JMH для 32-байтового байта [] не делаетНе будет большой разницы.

Benchmark              Mode  Cnt   Score   Error   Units
Main.original         thrpt    5  45.552 ± 3.580  ops/us
Main.usingByteBuffer  thrpt    5  39.968 ± 9.818  ops/us
Main.usingUnsafe      thrpt    5  60.660 ± 9.234  ops/us

Стоимость создания ByteBuffer и ShortBuffer будет менее важной для больших массивов, но это также указывает на другой способ ускорить решение, которое заключается в повторном использовании int[] но вместо этого возвращайте длину.

public static int unsafeTwoBytesToInts(byte[] bytes, int[] ints) {
    int len = bytes.length / 2;
    for (int i = 0; i < len; i++)
        ints[i] = Short.reverseBytes(
                unsafe.getShort(bytes, i * 2L + Unsafe.ARRAY_BYTE_BASE_OFFSET)) & 0xFFFF;
    return len;
}

с пропускной способностью

Main.usingUnsafe  thrpt    5  75.268 ± 9.119  ops/us
...