【问题标题】:Byte Array and Int conversion in JavaJava中的字节数组和Int转换
【发布时间】:2011-03-23 01:13:49
【问题描述】:

我在使用这两个函数时遇到了一些困难:byteArrayToIntintToByteArray

问题是,如果我使用一个到达另一个,而结果到达前者,结果会有所不同,如下面的示例所示。

我在代码中找不到错误。任何想法都非常受欢迎。谢谢。

public static void main(String[] args)
{
    int a = 123;
    byte[] aBytes = intToByteArray(a);
    int a2 = byteArrayToInt(aBytes);

    System.out.println(a);         // prints '123'
    System.out.println(aBytes);    // prints '[B@459189e1'
    System.out.println(a2);        // prints '2063597568
            System.out.println(intToByteArray(a2));  // prints '[B@459189e1'
}

public static int byteArrayToInt(byte[] b) 
{
    int value = 0;
    for (int i = 0; i < 4; i++) {
        int shift = (4 - 1 - i) * 8;
        value += (b[i] & 0x000000FF) << shift;
    }
    return value;
}

public static byte[] intToByteArray(int a)
{
    byte[] ret = new byte[4];
    ret[0] = (byte) (a & 0xFF);   
    ret[1] = (byte) ((a >> 8) & 0xFF);   
    ret[2] = (byte) ((a >> 16) & 0xFF);   
    ret[3] = (byte) ((a >> 24) & 0xFF);
    return ret;
}

【问题讨论】:

  • 尝试删除byteArrayToInt中的循环。

标签: java


【解决方案1】:

你的方法应该是(类似的)

public static int byteArrayToInt(byte[] b) 
{
    return   b[3] & 0xFF |
            (b[2] & 0xFF) << 8 |
            (b[1] & 0xFF) << 16 |
            (b[0] & 0xFF) << 24;
}

public static byte[] intToByteArray(int a)
{
    return new byte[] {
        (byte) ((a >> 24) & 0xFF),
        (byte) ((a >> 16) & 0xFF),   
        (byte) ((a >> 8) & 0xFF),   
        (byte) (a & 0xFF)
    };
}

这些方法已使用以下代码进行了测试:

Random rand = new Random(System.currentTimeMillis());
byte[] b;
int a, v;
for (int i=0; i<10000000; i++) {
    a = rand.nextInt();
    b = intToByteArray(a);
    v = byteArrayToInt(b);
    if (a != v) {
        System.out.println("ERR! " + a + " != " + Arrays.toString(b) + " != " + v);
    }
}
System.out.println("Done!");

【讨论】:

  • @owlstead,是的,您的代码最适合将byte[] 转换为int[],但是对于将byte 隔离转换为int,使用直接位操作(尤其是当您知道期望定义的数据正确形成)比创建一个类的实例来做完全相同的事情要快得多。每次通话。即使您缓存ByteBuffer 的实例,您仍然必须将传递的字节复制到缓冲区的数组中,因此无论如何都会给您带来一些开销。
  • @owlstead 也许可以,但是如果您可以从一开始就优化一种方法,而且它非常简单,您为什么会满足于更少呢?您在这里不需要可重用的模式。此外,“支付小代价”的积累可以在一个大项目中迅速升级。想想吧。
  • 是的,这个答案已经很老了。我不知道为什么我在第一种方法中使用了for 块...
  • 我有点困惑。在intToByteArray 方法中,为什么在将每个字节转换回一个字节之前必须对每个字节进行AND0xFF? 0xFF 将包括所有 8 位。一个字节只有8位。所以代码`&0xFF`似乎毫无意义。
  • @WaffleStealer654:这是必需的,因为 Java 字节是有符号的,所以如果最高位是负数,当转换为 int 时,它是一个负数,所有高位都有一位。 (在我看来,Java 字节被签名的事实是礼貌的,不是最佳的,但事实就是这样。)
【解决方案2】:

这是很多工作:

public static int byteArrayToLeInt(byte[] b) {
    final ByteBuffer bb = ByteBuffer.wrap(b);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    return bb.getInt();
}

public static byte[] leIntToByteArray(int i) {
    final ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    bb.putInt(i);
    return bb.array();
}

此方法使用 java.nio 包中的 Java ByteBufferByteOrder 功能。在需要可读性时,应首选此代码。它也应该很容易记住。

我在这里展示了 Little Endian 字节顺序。要创建 Big Endian 版本,您只需忽略对 order(ByteOrder) 的调用即可。


在性能优先于可读性的代码中(大约快 10 倍):

public static int byteArrayToLeInt(byte[] encodedValue) {
    int value = (encodedValue[3] << (Byte.SIZE * 3));
    value |= (encodedValue[2] & 0xFF) << (Byte.SIZE * 2);
    value |= (encodedValue[1] & 0xFF) << (Byte.SIZE * 1);
    value |= (encodedValue[0] & 0xFF);
    return value;
}

public static byte[] leIntToByteArray(int value) {
    byte[] encodedValue = new byte[Integer.SIZE / Byte.SIZE];
    encodedValue[3] = (byte) (value >> Byte.SIZE * 3);
    encodedValue[2] = (byte) (value >> Byte.SIZE * 2);   
    encodedValue[1] = (byte) (value >> Byte.SIZE);   
    encodedValue[0] = (byte) value;
    return encodedValue;
}

只需反转字节数组索引以从零计数到三以创建此代码的 Big Endian 版本。


注意事项:

  • 在 Java 8 中,您还可以使用 Integer.BYTES 常量,它比 Integer.SIZE / Byte.SIZE 更简洁。

【讨论】:

  • 一个班轮:byte[] result = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putInt(i).array();
  • 好的,所以与 Yanick 就性能进行了有趣的讨论,所以我添加了一个性能增强版本,使用 Yanick 的答案(已投票)作为基础。最重要的变化当然是删除了for 循环,因为当前CPU 上的分支速度很慢。因此,它至少快两倍。
  • 你的短版和性能版之间没有不一致吗?排序版是小端,性能版是大端?
  • @RenniePet 啊,是的,应该更清楚地说明这一点。稍后我会添加性能版本的 LE 版本。
  • byteArrayToLeInt的性能版本,你为什么不和0xffencodedValue[3]呢?
【解决方案3】:

您正在两种方法之间交换endianness。您有intToByteArray(int a) 将低位分配给ret[0],但随后byteArrayToInt(byte[] b)b[0] 分配给结果的高位。您需要颠倒其中一个,例如:

public static byte[] intToByteArray(int a)
{
    byte[] ret = new byte[4];
    ret[3] = (byte) (a & 0xFF);   
    ret[2] = (byte) ((a >> 8) & 0xFF);   
    ret[1] = (byte) ((a >> 16) & 0xFF);   
    ret[0] = (byte) ((a >> 24) & 0xFF);
    return ret;
}

【讨论】:

  • 看看ByteBuffer class,我已经围绕它创建了一个示例。
【解决方案4】:

您还可以将 BigInteger 用于可变长度字节。您可以根据需要将其转换为 Long、Integer 或 Short。

new BigInteger(bytes).intValue();

或表示极性:

new BigInteger(1, bytes).intValue();

只取回字节:

new BigInteger(bytes).toByteArray()

【讨论】:

  • 很好、简单、灵活的答案。谢谢!
  • @Duffmaster33 简单,但速度慢……而且垃圾收集压力太大。但无论如何 +1 :)
  • @St.Antario 感谢您的评论,您介意详细说明一下吗?我很好奇
  • @Duffmaster33 如果我们运行简单的JMH-benchmark,就可以看到这一点。我用-prof gc 运行它,看到在单个fork 5 热身和5 次迭代中使用默认参数,我得到了16 次Minor GC,分配率500 MB/sec。在我的机器上它慢了大约 10 倍。
  • @Duffmaster33 我刚刚用按位运算反编译了版本,发现所有这些movsbl 0x13(%r11),%r10d movsbl 0x11(%r11),%eax movsbl 0x12(%r11),%r8d movzbl 0x10(%r11),%r9d 和这些都是or-ed。
【解决方案5】:

我喜欢 owlstead 的原始答案,如果您不喜欢在每个方法调用上创建 ByteBuffer 的想法,那么您可以通过调用 .clear().flip() 方法来重用 ByteBuffer

ByteBuffer _intShifter = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE)
                                   .order(ByteOrder.LITTLE_ENDIAN);

public byte[] intToByte(int value) {
    _intShifter.clear();
    _intShifter.putInt(value);      
    return _intShifter.array();
}

public int byteToInt(byte[] data)
{
    _intShifter.clear();
    _intShifter.put(data, 0, Integer.SIZE / Byte.SIZE);
    _intShifter.flip();
    return _intShifter.getInt();
}

【讨论】:

  • 是的,只是不要在多线程代码中重复使用缓冲区,那会给你带来麻烦。
【解决方案6】:

我在 [Maven:com.google.guava:guava:12.0.1] 中的 com.google.common.primitives 中找到了一种简单的方法

long newLong = Longs.fromByteArray(oldLongByteArray);
int newInt = Ints.fromByteArray(oldIntByteArray);

试一试:)

【讨论】:

    【解决方案7】:

    我仔细看了很多这样的问题,发现this post...我不喜欢每种类型的转换代码都是重复的,所以我做了一个通用的方法来执行任务:

    public static byte[] toByteArray(long value, int n)
    {
        byte[] ret = new byte[n];
        ret[n-1] = (byte) ((value >> (0*8) & 0xFF);   
        ret[n-2] = (byte) ((value >> (1*8) & 0xFF);   
        ...
        ret[1] = (byte) ((value >> ((n-2)*8) & 0xFF);   
        ret[0] = (byte) ((value >> ((n-1)*8) & 0xFF);   
        return ret;
    }
    

    full post

    【讨论】:

    • 显然它循环...不是手工制作的:)
    【解决方案8】:

    /*对不起,这是正确的*/

         public byte[] IntArrayToByteArray(int[] iarray , int sizeofintarray)
         {
           final ByteBuffer bb ;
           bb = ByteBuffer.allocate( sizeofintarray * 4);
           for(int k = 0; k < sizeofintarray ; k++)
           bb.putInt(k * 4, iar[k]);
           return bb.array();
         }
    

    【讨论】:

    • 欢迎来到 SO。解释会改善这个答案。
    【解决方案9】:

    不是分配空间等,而是使用ByteBuffer from java.nio....的方法。

    byte[] arr = { 0x01, 0x00, 0x00, 0x00, 0x48, 0x01};
    
    // say we want to consider indices 1, 2, 3, 4 {0x00, 0x00, 0x00, 0x48};
    ByteBuffer bf = ByteBuffer.wrap(arr, 1, 4); // big endian by default
    int num = bf.getInt();    // 72
    

    现在,走另一条路。

    ByteBuffer newBuf = ByteBuffer.allocate(4);
    newBuf.putInt(num);
    byte[] bytes = newBuf.array();  // [0, 0, 0, 72] {0x48 = 72}
    

    【讨论】:

      【解决方案10】:

      这是我的实现

      public static byte[] intToByteArray(int a) {
          return BigInteger.valueOf(a).toByteArray();
      }
      
      public static int byteArrayToInt(byte[] b) {
          return new BigInteger(b).intValue();
      }
      

      【讨论】:

      • -1,这将返回与要求不同的结果(它返回最小字节数而不是 4 个字节)。此外,对于这种目的,使用 BigInteger 有点繁重。
      • 虽然没有我预期的那么糟糕,只比优化版本慢 20 倍 :),我必须承认我的速度并没有那么快。
      猜你喜欢
      • 2017-08-22
      • 2011-09-16
      • 2022-06-20
      • 1970-01-01
      • 2014-12-27
      • 2011-09-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多