170

I want to store some data into byte arrays in Java. Basically just numbers which can take up to 2 Bytes per number.

I'd like to know how I can convert an integer into a 2 byte long byte array and vice versa. I found a lot of solutions googling but most of them don't explain what happens in the code. There's a lot of shifting stuff I don't really understand so I would appreciate a basic explanation.

Wrigglenite
  • 119
  • 7
Chris
  • 7,675
  • 8
  • 51
  • 101
  • 4
    How much *do* you understand about bit shifting? It sounds like the question is really "what does bit shifting do" more than about the conversion to byte arrays, really - if you actually want to understand how the conversion would work. – Jon Skeet Oct 01 '11 at 08:20
  • 1
    (Just to clarify, I'm fine with either question, but it's worth making it clear *which* question you really want answered. You're likely to get an answer which is more useful to you that way.) – Jon Skeet Oct 01 '11 at 08:21
  • Okay i got your point! Thanks for the remark. I know what bit shifting does i just didn't understand what its used for in converting byte arrays yet. – Chris Oct 02 '11 at 12:23
  • 3
    @prekageo and Jeff Mercado Thanks for your two answers. prekageo gave a good explanation of how this is done, nice link! That makes it a lot clearer to me. And Jeff Mercados solution solved the problem i had. – Chris Oct 02 '11 at 12:28

8 Answers8

261

Use the classes found in the java.nio namespace, in particular, the ByteBuffer. It can do all the work for you.

byte[] arr = { 0x00, 0x01 };
ByteBuffer wrapped = ByteBuffer.wrap(arr); // big-endian by default
short num = wrapped.getShort(); // 1

ByteBuffer dbuf = ByteBuffer.allocate(2);
dbuf.putShort(num);
byte[] bytes = dbuf.array(); // { 0, 1 }
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • 4
    Is it too expensive if the byte array contains only 1 or 2 integer? Not sure about the cost constructing a `ByteBuffer`. – Meow Cat 2012 Mar 02 '19 at 03:01
  • How often are you working with binary data in 2-4 byte chunks? Really? A sane implementation would either work with it in BUFSIZ chunks (typically 4kb) or use other IO libraries that hides this detail. There's an entire library within the framework that's dedicated to help you work on buffers of data. You do a disservice to yourself and other maintainers of your code when you implement common operations without good reason (be it perf or other critical operation). These buffers are merely wrappers that operates on arrays, nothing more. – Jeff Mercado May 13 '19 at 04:26
  • How come you can instantiate an abstract class? –  Jul 31 '19 at 23:13
  • 2
    @JaveneCPPMcGowan There is no direct instantiation present in this answer. If you mean the factory methods `wrap` and `allocate`, they don't return an instance of the abstract class `ByteBuffer`. – Marko Topolnik Dec 18 '19 at 13:03
  • Not a solution for 3 byte stride. We can get `Char`, `Short`, `Int`. I suppose I could pad to 4 bytes and discard the 4th each time, but I would rather not. – John Jun 13 '20 at 07:31
  • @John: Well you can only really work with builtin types using this approach. A 3-byte integral type is definitely non-standard. But you can still work with it this way like you can any mainstream language. You just need to read smaller portions of the data and bit-manipulate it to your desired value. – Jeff Mercado Jun 13 '20 at 07:37
157
byte[] toByteArray(int value) {
     return  ByteBuffer.allocate(4).putInt(value).array();
}

byte[] toByteArray(int value) {
    return new byte[] { 
        (byte)(value >> 24),
        (byte)(value >> 16),
        (byte)(value >> 8),
        (byte)value };
}

int fromByteArray(byte[] bytes) {
     return ByteBuffer.wrap(bytes).getInt();
}
// packing an array of 4 bytes to an int, big endian, minimal parentheses
// operator precedence: <<, &, | 
// when operators of equal precedence (here bitwise OR) appear in the same expression, they are evaluated from left to right
int fromByteArray(byte[] bytes) {
     return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
}

// packing an array of 4 bytes to an int, big endian, clean code
int fromByteArray(byte[] bytes) {
     return ((bytes[0] & 0xFF) << 24) | 
            ((bytes[1] & 0xFF) << 16) | 
            ((bytes[2] & 0xFF) << 8 ) | 
            ((bytes[3] & 0xFF) << 0 );
}

When packing signed bytes into an int, each byte needs to be masked off because it is sign-extended to 32 bits (rather than zero-extended) due to the arithmetic promotion rule (described in JLS, Conversions and Promotions).

There's an interesting puzzle related to this described in Java Puzzlers ("A Big Delight in Every Byte") by Joshua Bloch and Neal Gafter . When comparing a byte value to an int value, the byte is sign-extended to an int and then this value is compared to the other int

byte[] bytes = (…)
if (bytes[0] == 0xFF) {
   // dead code, bytes[0] is in the range [-128,127] and thus never equal to 255
}

Note that all numeric types are signed in Java with exception to char being a 16-bit unsigned integer type.

Jarek Przygódzki
  • 4,284
  • 2
  • 31
  • 41
  • I think the `& 0xFF`s are unnecessary. – Ori Popowski Mar 09 '14 at 20:20
  • 13
    @LeifEricson I believe the `& 0xFF`s are necessary as it tells the JVM to convert the signed byte into an integer with just those bits set. Otherwise the byte -1 (0xFF) will turn into the int -1 (0xFFFFFFFF). I could be wrong and even if I am it doesn't hurt and makes things clearer. – coderforlife Jul 29 '14 at 18:44
  • 4
    & 0xFF is mandatory indeed. `byte b = 0; b |= 0x88; System.out.println(Integer.toString(b, 16)); //Output: -78 System.out.println(Integer.toString(b & 0xFF, 16)); //Output: 88` – HBN Dec 06 '14 at 17:54
  • Ej, to jest Polak! :D – barwnikk Jan 06 '15 at 21:56
  • Is it that, `& 0xFF`s are converting the signed byte to the unsigned byte? – ptntialunrlsd Nov 03 '15 at 06:22
  • 1
    @ptntialunrlsd Not actually. Before you performing **&** operation on `byte` with 0xFF (`int`), JVM will cast the `byte` to `int` with **1 extended** or **0 extended** according the the leading bit first. There's no **unsigned byte** in Java, `byte`s are always signed. – Nier Mar 24 '16 at 03:35
  • 2
    When parse int from byte array, pay attention to the size of the byte array, if it's greater than 4 bytes, according to the doc of `ByteBuffer.getInt()` : `Reads the next four bytes at this buffer's current position` , only the first 4 bytes will be parsed, which should not be what you want. – Bin May 05 '17 at 06:46
  • To complement [Bin's comment](https://stackoverflow.com/questions/7619058/convert-a-byte-array-to-integer-in-java-and-vice-versa/7619111#comment74636539_7619315) - if the bytes array has less than 2 bytes, adjust the code accordingly, i.e. make sure to allocate enough capacity and fill buffer's left-most positions with zeroes. For source array `byte[] int257 = {1,1};` running `ByteBuffer bb = ByteBuffer.allocate(4); bb.put(int257); bb.getInt(0)` will gives you 16842752 while `ByteBuffer bb = ByteBuffer.allocate(4); bb.put((byte)0); bb.put((byte)0); bb.put(int257); bb.getInt(0)` gives 257. – GreenhouseVeg Jul 10 '18 at 12:56
72

You can also use BigInteger for variable length bytes. You can convert it to long, int 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()

Although simple, I just wanted to point out that if you run this many times in a loop, this could lead to a lot of garbage collection. This may be a concern depending on your use case.

Jamel Toms
  • 4,525
  • 2
  • 27
  • 26
6

A basic implementation would be something like this:

public class Test {
    public static void main(String[] args) {
        int[] input = new int[] { 0x1234, 0x5678, 0x9abc };
        byte[] output = new byte[input.length * 2];

        for (int i = 0, j = 0; i < input.length; i++, j+=2) {
            output[j] = (byte)(input[i] & 0xff);
            output[j+1] = (byte)((input[i] >> 8) & 0xff);
        }

        for (int i = 0; i < output.length; i++)
            System.out.format("%02x\n",output[i]);
    }
}

In order to understand things you can read this WP article: http://en.wikipedia.org/wiki/Endianness

The above source code will output 34 12 78 56 bc 9a. The first 2 bytes (34 12) represent the first integer, etc. The above source code encodes integers in little endian format.

prekageo
  • 358
  • 3
  • 15
5
/** length should be less than 4 (for int) **/
public long byteToInt(byte[] bytes, int length) {
        int val = 0;
        if(length>4) throw new RuntimeException("Too big to fit in int");
        for (int i = 0; i < length; i++) {
            val=val<<8;
            val=val|(bytes[i] & 0xFF);
        }
        return val;
    }
Dhaval Rami
  • 51
  • 1
  • 2
4

As often, guava has what you need.

To go from byte array to int: Ints.fromBytesArray, doc here

To go from int to byte array: Ints.toByteArray, doc here

jeremie
  • 971
  • 9
  • 19
0

Someone with a requirement where they have to read from bits, lets say you have to read from only 3 bits but you need signed integer then use following:

data is of type: java.util.BitSet

new BigInteger(data.toByteArray).intValue() << 32 - 3 >> 32 - 3

The magic number 3 can be replaced with the number of bits (not bytes) you are using.

Vishrant
  • 15,456
  • 11
  • 71
  • 120
-7

i think this is a best mode to cast to int

   public int ByteToint(Byte B){
        String comb;
        int out=0;
        comb=B+"";
        salida= Integer.parseInt(comb);
        out=out+128;
        return out;
    }

first comvert byte to String

comb=B+"";

next step is comvert to a int

out= Integer.parseInt(comb);

but byte is in rage of -128 to 127 for this reasone, i think is better use rage 0 to 255 and you only need to do this:

out=out+256;
mtk
  • 13,221
  • 16
  • 72
  • 112
  • This is wrong. Consider the byte 0x01. Your method will output 129 which is wrong. 0x01 should output the integer 1. You should only add 128 if the integer that you get from parseInt is negative. – disklosr May 09 '14 at 08:53
  • I meant you should add 256 not 128. Couldn't edit it afterwards. – disklosr May 09 '14 at 09:01
  • changed post to add the 256 as it may be useful to others! – apmartin1991 Feb 16 '15 at 10:21
  • This does a lot of casting and create new objects (think doing this on for loops) that can degrade the performance, please check the Integer.toString() method for hints about how to parse numbers. – Marcos Vasconcelos Oct 01 '15 at 20:49
  • also, when posting code on stackoverflow the point is to post code that easily makes sense. Code that easily makes sense must have *understandable* identifiers. And on stackoverflow, *understandable* necessarily means *in English*. – Mike Nakis Dec 30 '19 at 11:25