Contents

Java中字节数组转换为数字

1. 概述

在本教程中,我们将探讨将*byte[]*转换为数值(intlongfloatdouble)的不同方法,反之亦然。

字节是计算机存储和处理信息的基本单位。Java 语言中定义的基本类型 是同时操作多个字节的便捷方式。因此,*byte[]*和原始类型之间存在着一种内在的转换关系。

由于shortchar类型只包含两个字节,因此不需要太多关注。因此,我们将重点关注byte[]intlongfloatdouble类型之间的转换。

2. 使用移位运算符

将*byte[]*转换为数值的最直接方法是使用移位运算符

2.1. 字节数组到intlong

byte[]转换为int值时,我们使用*«*(左移)运算符:

int value = 0;
for (byte b : bytes) {
    value = (value << 8) + (b & 0xFF);
}

通常,上述代码片段中byte[]的长度应等于或小于四。那是因为一个int值占用四个字节。否则会导致int范围溢出。

为了验证转换的正确性,我们定义两个常量:

byte[] INT_BYTE_ARRAY = new byte[] {
    (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE
};
int INT_VALUE = 0xCAFEBABE;

如果我们仔细观察这两个常量INT_BYTE_ARRAYINT_VALUE,我们会发现它们是十六进制数0xCAFEBABE的不同表示。

然后,让我们检查一下这个转换是否正确:

int value = convertByteArrayToIntUsingShiftOperator(INT_BYTE_ARRAY);
assertEquals(INT_VALUE, value);

类似地,当将一个byte[]转换为一个long值时,我们可以重用上面的代码片段并进行两处修改:值的类型为long型且字节长度应等于或小于八。

2.2. intlong到字节数组

int值转换为byte[]时,我们可以使用»(有符号右移)或*»>*(无符号右移)运算符:

byte[] bytes = new byte[Integer.BYTES];
int length = bytes.length;
for (int i = 0; i < length; i++) {
    bytes[length - i - 1] = (byte) (value & 0xFF);
    value >>= 8;
}

在上面的代码片段中,我们可以将*»运算符替换为»>运算符。那是因为我们只使用了value*参数最初包含的字节。因此,带符号扩展或零扩展的右移不会影响最终结果。

然后,我们可以检查上面转换的正确性:

byte[] bytes = convertIntToByteArrayUsingShiftOperator(INT_VALUE);
assertArrayEquals(INT_BYTE_ARRAY, bytes);

long值转换为byte[]时,我们只需要将Integer.BYTES更改为Long.BYTES并确保值的类型为long

2.3. 字节数组、floatdouble

*byte[]转换为float 时,我们使用*Float.intBitsToFloat()方法

// convert bytes to int
int intValue = 0;
for (byte b : bytes) {
    intValue = (intValue << 8) + (b & 0xFF);
}
// convert int to float
float value = Float.intBitsToFloat(intValue);

从上面的代码片段中,我们可以了解到byte[]不能直接转换为float值。基本上,它需要两个独立的步骤:首先,我们将byte[]转换为int值,然后将相同的位模式解释为float值。

为了验证转换的正确性,我们定义两个常量:

byte[] FLOAT_BYTE_ARRAY = new byte[] {
    (byte) 0x40, (byte) 0x48, (byte) 0xF5, (byte) 0xC3
};
float FLOAT_VALUE = 3.14F;

然后,让我们检查一下这个转换是否正确:

float value = convertByteArrayToFloatUsingShiftOperator(FLOAT_BYTE_ARRAY);
assertEquals(Float.floatToIntBits(FLOAT_VALUE), Float.floatToIntBits(value));

同样,我们可以利用中间long值和Double.longBitsToDouble()方法将byte[]转换为double

2.4. floatdouble到字节数组

*float转换为*byte[]时,我们可以利用Float.floatToIntBits()方法

// convert float to int
int intValue = Float.floatToIntBits(value);
// convert int to bytes
byte[] bytes = new byte[Float.BYTES];
int length = bytes.length;
for (int i = 0; i < length; i++) {
    bytes[length - i - 1] = (byte) (intValue & 0xFF);
    intValue >>= 8;
}

然后,让我们检查一下这个转换是否正确:

byte[] bytes = convertFloatToByteArrayUsingShiftOperator(FLOAT_VALUE);
assertArrayEquals(FLOAT_BYTE_ARRAY, bytes);

以此类推,我们可以利用Double.doubleToLongBits()方法将double值转换为byte[]

3. 使用ByteBuffer

*java.nio.ByteBuffer类提供了一种简洁、统一的方式来在*byte[]和数值(intlongfloatdouble)之间进行转换。

3.1. 字节数组到数值

现在,我们使用ByteBuffer类将byte[]转换为int值:

ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
buffer.put(bytes);
buffer.rewind();
int value = buffer.getInt();

然后,我们使用ByteBuffer类将int值转换为byte[]

ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
buffer.putInt(value);
buffer.rewind();
byte[] bytes = buffer.array();

我们应该注意到上面的两个代码片段遵循相同的模式:

  • 首先,我们使用ByteBuffer.allocate(int)方法获取指定容量的ByteBuffer对象。
  • 然后,我们将原始值(byte[]int值)放入ByteBuffer对象,例如*buffer.put(bytes)buffer.putInt(value)*方法。
  • 之后,我们将ByteBuffer对象的位置重置为零,以便我们可以从头开始读取。
  • 最后,我们使用buffer.getInt()buffer.array()等方法从ByteBuffer对象中获取目标值。

这种模式非常通用,它支持longfloatdouble类型的转换。我们唯一需要做的修改是与类型相关的信息。

3.2. 使用现有的字节数组

此外,*ByteBuffer.wrap(byte[])方法允许我们重用现有byte[]*而无需创建新数组:

ByteBuffer.wrap(bytes).getFloat();

但是,我们还应该注意,上面的bytes变量的长度等于或大于目标类型(Float.BYTES)的大小。否则,它将抛出BufferUnderflowException

4. 使用BigInteger

java.math.BigInteger 类的主要目的是表示大数值,否则这些数值将不适合原始数据类型。尽管我们可以使用它在byte[]和原始值之间进行转换,但使用BigInteger对于这种目的来说有点繁重。

4.1. 字节数组到intlong

现在,让我们使用BigInteger类将byte[]转换为int值:

int value = new BigInteger(bytes).intValue();

类似地,BigInteger类有一个longValue()方法来将byte[]转换为long值:

long value = new BigInteger(bytes).longValue();

此外,BigInteger类还有一个intValueExact()方法和一个longValueExact()方法。应谨慎使用这两个方法:如果BigInteger对象分别超出intlong类型的范围,则这两个方法都将抛出ArithmeticException

intlong值转换为*byte[]*时,我们可以使用相同的代码片段:

byte[] bytes = BigInteger.valueOf(value).toByteArray();

但是,BigInteger类的*toByteArray()*方法返回的是最小字节数,不一定是四个或八个字节。

4.2. 字节数组、floatdouble

虽然BigInteger类有一个floatValue()方法,但我们不能像预期的那样使用它来将byte[]转换为float值。那么,我们应该怎么做呢?我们可以使用int值作为将byte[]转换为float值的中间步骤:

int intValue = new BigInteger(bytes).intValue();
float value = Float.intBitsToFloat(intValue);

同样,我们可以将float值转换为byte[]

int intValue = Float.floatToIntBits(value);
byte[] bytes = BigInteger.valueOf(intValue).toByteArray();

同样,通过利用Double.longBitsToDouble()Double.doubleToLongBits()方法,我们可以使用BigInteger类在byte[]double值之间进行转换。

5. 使用Guava

Guava 库为我们提供了方便的方法来进行这种转换。

5.1. 字节数组到intlong

在 Guava 中,com.google.common.primitives包中的Ints类包含一个fromByteArray()方法。因此,我们很容易将byte[]转换为int值:

int value = Ints.fromByteArray(bytes);

Ints类还有一个toByteArray()方法,可用于将int值转换为byte[]

byte[] bytes = Ints.toByteArray(value);

而且,Longs类在使用上类似于Ints类:

long value = Longs.fromByteArray(bytes);
byte[] bytes = Longs.toByteArray(value);

此外,如果我们检查*fromByteArray()toByteArray()*方法的源代码,我们可以发现这两种方法都使用移位运算符来完成它们的任务

5.2. 字节数组、floatdouble

同一个包中还存在FloatsDoubles类。但是,这两个类都不支持*fromByteArray()toByteArray()*方法。

但是,我们可以利用Float.intBitsToFloat()Float.floatToIntBits()Double.longBitsToDouble()Double.doubleToLongBits()方法来完成byte[]floatdouble值之间的转换。为简洁起见,我们省略了此处的代码。

6. 使用 Commons Lang

当我们使用Apache Commons Lang 3 时,进行这些类型的转换有点复杂。这是因为Commons Lang 库默认使用小端byte[]。但是,我们上面提到的byte[]都是大端顺序的。因此,我们需要将大端byte[]转换为小端byte[],反之亦然。

6.1. 字节数组到intlong

org.apache.commons.lang3包中的 Conversion 类提供了*byteArrayToInt()intToByteArray()*方法。

现在,让我们将byte[]转换为int值:

byte[] copyBytes = Arrays.copyOf(bytes, bytes.length);
ArrayUtils.reverse(copyBytes);
int value = Conversion.byteArrayToInt(copyBytes, 0, 0, 0, copyBytes.length);

*在上面的代码中,我们复制了原始byte变量。这是因为有时候,我们不想改变原始*byte[]的内容。

然后,让我们将一个int值转换为一个byte[]

byte[] bytes = new byte[Integer.BYTES];
Conversion.intToByteArray(value, 0, bytes, 0, bytes.length);
ArrayUtils.reverse(bytes);

Conversion类还定义了byteArrayToLong()longToByteArray()方法。并且,我们可以使用这两种方法在byte[]long值之间进行转换。

6.2. 字节数组、floatdouble

但是,Conversion类并没有直接提供相应的方法来转换floatdouble值。

同样,我们需要一个中间intlong值来在byte[]floatdouble值之间进行转换。