【问题标题】:Java convert byte[] to int[] by concatenate every two bytesJava 通过连接每两个字节将 byte[] 转换为 int[]
【发布时间】:2019-05-24 06:56:55
【问题描述】:

我有一个byte[ ] 数组。我如何连接每 2 个字节(为每个人获得 16 位短尺寸值)并转换为 int

我正在使用 for 循环进行转换,但我的字节数组很长,所以 for 循环让它变慢了。

例如:

字节数组如:{0x00, 0x01, 0x01, 0x02, 0x03, 0x04}

我想连接每 2 个字节,如下所示:{0x0001, 0x0102, 0x0304}

然后得到一个int[]数组,像这样:{ 1, 258, 772 }等等……

简而言之,通过连接两个字节获得短尺寸(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 库来加快这个过程吗?

谢谢。

【问题讨论】:

  • 您不太可能使其更快。无论您使用什么库,都可能会在内部做同样的事情。如果您的应用程序允许,另一种选择是懒惰地执行此操作。具体来说,有一些intBufferGet(i) 函数将返回(buffer[2*i] &lt;&lt; 8) | buffer[2*i+1]。这样您就不必预先计算所有intBuffer。这是否会起作用/帮助取决于应用程序。
  • 感谢@arshajii,但我每次都需要完整的数组。实际上,这个字节数组是流的一部分,我需要不断地将它转换为 int 数组。
  • 你可以用一个可以转换为 ShortBuffer 的 ByteBuffer 来包装它。这一次读取两个字节并避免移位。如果您需要更快,您可能会使用 Unsafe。
  • 我可以使用ByteBuffer.wrap(buffer).asShortBuffer() 获得 ShortBuffer 和 short[ ] 数组。这给了我 ShortBuffer,但我仍然需要 int[] 用于图像处理。我首先从您那里听说了 Unsafe,我看了一点,但不知道在数组操作中使用它。感谢您的建议@PeterLawrey。

标签: java arrays int byte short


【解决方案1】:

使用 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;
}

通过 32 字节 byte[] 的 JMH 基准运行这些测试并没有显示出太大差异。

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

【讨论】:

  • unsafe.getShort 调用不会容易出现未对齐的内存访问吗?
  • @E_net4wisheshappyholidays 根据 JVM,这是可能的,但是在基于 OpenJDK 的 JVM 中,byte[] 中的字节始终是 32 位对齐的。在 x86、x64、ARM 上,未对齐的 short 读取正常(可能慢一点)
  • 不安全的版本还需要一个&amp; 0xFFFF 来将short 转换为unsigned int。顺便说一句,我想知道为什么i * 2L + BASE_OFFSETi * 2 + BASE_OFFSET 在性能上有显着差异?
  • @yyyy 不同之处在于不分配int[] 而不是使用long
猜你喜欢
  • 1970-01-01
  • 2014-05-04
  • 2011-02-19
  • 1970-01-01
  • 1970-01-01
  • 2013-01-13
  • 2011-06-24
  • 1970-01-01
  • 2012-02-05
相关资源
最近更新 更多