I am having some difficulty with these two functions: byteArrayToInt
and intToByteArray
.
The problem is that if I use one to get to another and that result to get to the former, the results are different, as you can see from my examples below.
I cannot find the bug in the code. Any ideas are very welcome. Thanks.
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;
}
The byteValue() method of Integer class of java. lang package converts the given Integer into a byte after a narrowing primitive conversion and returns it (value of integer object as a byte). Also, remember this method does override byteValue() method of the Number class.
The intValue() method of Byte class is a built in method in Java which is used to return the value of this Byte object as int.
Convert byte[] array to File using Java In order to convert a byte array to a file, we will be using a method named the getBytes() method of String class. Implementation: Convert a String into a byte array and write it in a file.
byte datatype has a range from -128 to 127 and it requires very little memory (only 1 byte). It can be used in place of int where we are sure that the range will be very small. The compiler automatically promotes the byte variables to type int, if they are used in an expression and the value exceeds their range.
Your methods should be (something like)
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)
};
}
These methods were tested with the following code :
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!");
That's a lot of work for:
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();
}
This method uses the Java ByteBuffer
and ByteOrder
functionality in the java.nio
package. This code should be preferred where readability is required. It should also be very easy to remember.
I've shown Little Endian byte order here. To create a Big Endian version you can simply leave out the call to order(ByteOrder)
.
In code where performance is higher priority than readability (about 10x as fast):
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;
}
Just reverse the byte array index to count from zero to three to create a Big Endian version of this code.
Notes:
Integer.BYTES
constant, which is more succinct than Integer.SIZE / Byte.SIZE
.You're swapping endianness between your two methods. You have intToByteArray(int a)
assigning the low-order bits into ret[0]
, but then byteArrayToInt(byte[] b)
assigns b[0]
to the high-order bits of the result. You need to invert one or the other, like:
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;
}
You can also use BigInteger for variable length bytes. You can convert it to Long, Integer or Short, whichever suits your needs.
new BigInteger(bytes).intValue();
or to denote polarity:
new BigInteger(1, bytes).intValue();
To get bytes back just:
new BigInteger(bytes).toByteArray()
I like owlstead's original answer, and if you don't like the idea of creating a ByteBuffer
on every method call then you can reuse the ByteBuffer
by calling it's .clear()
and .flip()
methods:
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();
}
I found a simple way in com.google.common.primitives which is in the [Maven:com.google.guava:guava:12.0.1]
long newLong = Longs.fromByteArray(oldLongByteArray);
int newInt = Ints.fromByteArray(oldIntByteArray);
Have a nice try :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With