4

I am sending byte[] arrays over a socket connection in Java.

I have a pretty long boolean[] array, where array.length % 8 == 0.

I'd like to convert this boolean[] array into a byte[] array with 8 times as few elements, so that I can then send the byte[] over the socket connection.

The boolean[] array looks like this: 01011010 10101010 01100011 11001010 etc.

The byte[] in this case should look like this: 0x5A 0xAA 0x63 0xCA.

I have found some code on another question on how to convert a single byte into a boolean[] array and added a new method to it to convert an entire array here:

public static boolean[] booleanArrayFromByteArray(byte[] x) {
    boolean[] y = new boolean[x.length * 8];
    int position = 0;
    for(byte z : x) {
        boolean[] temp = booleanArrayFromByte(z);
        System.arraycopy(temp, 0, y, position, 8);
        position += 8;
    }
    return y;
}

public static boolean[] booleanArrayFromByte(byte x) {
    boolean bs[] = new boolean[4];
    bs[0] = ((x & 0x01) != 0);
    bs[1] = ((x & 0x02) != 0);
    bs[2] = ((x & 0x04) != 0);
    bs[3] = ((x & 0x08) != 0);
    return bs;
}

I'd like to know if there is a more efficient way of doing this.

Edit: Thanks

4 Answers4

6

There already exists a class (BitSet) in the standard library to help you do this, you should use that instead of a boolean array. The class allows you to get and set bits and to various logical operations on your boolean values as a group.

eg.

BitSet bits = BitSet.valueOf(bytes); 
boolean third_bit = bits.get(3);
bits.set(5, false);
byte[] new_bytes = bits.toByteArray();

If you really need to use a boolean array then the following will work.

static boolean[] toBooleanArray(byte[] bytes) {
    BitSet bits = BitSet.valueOf(bytes);
    boolean[] bools = new boolean[bytes.length * 8];
    for (int i = bits.nextSetBit(0); i != -1; i = bits.nextSetBit(i+1)) {
        bools[i] = true;
    }
    return bools;
}

static byte[] toByteArray(boolean[] bools) {
    BitSet bits = new BitSet(bools.length);
    for (int i = 0; i < bools.length; i++) {
        if (bools[i]) {
            bits.set(i);
        }
    }

    byte[] bytes = bits.toByteArray();
    if (bytes.length * 8 >= bools.length) {
        return bytes;
    } else {
        return Arrays.copyOf(bytes, bools.length / 8 + (bools.length % 8 == 0 ? 0 : 1));
    }
}
Dunes
  • 37,291
  • 7
  • 81
  • 97
  • 2
    This will ignore all false booleans. Ie: An array of booleans that are all false, will get an empty byte array. – Craigo Apr 06 '16 at 11:55
  • 1
    Hmm, yes, this will ignore all higher zero bits after the last one bit. Not much effort to create a full sized empty byte array and copy over the bits byte array though. – Dunes Apr 06 '16 at 12:57
  • With Java 8, in the "toBooleanArray(...)" method, you could replace the "for" loop with "bits.stream().forEach(bit -> bools[bit] = true);". It's a bit more succinct for the same work. (Not a really big deal, though.) – pfurbacher Feb 21 '17 at 18:59
3

You could do it something like this.

public byte[] toBytes(boolean[] input) {
    byte[] toReturn = new byte[input.length / 8];
    for (int entry = 0; entry < toReturn.length; entry++) {
        for (int bit = 0; bit < 8; bit++) {
            if (input[entry * 8 + bit]) {
                toReturn[entry] |= (128 >> bit);
            }
        }
    }

    return toReturn;
} 

This relies on the fact that toReturn will be initialised with all zeroes. Then for each true that we encounter in input, we set the appropriate bit within the appropriate entry in toReturn.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
2

For converting bytes to booleans:

public static boolean [] bytesToBooleans(byte [] bytes){
    boolean [] bools = new boolean[bytes.length * 8];
    byte [] pos = new byte[]{(byte)0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1};

    for(int i = 0; i < bytes.length; i++){
        for(int j = i * 8, k = 0; k < 8; j++, k++){
            bools[j] = (bytes[i] & pos[k]) != 0;
        }
    }

    return bools;
}

Or another method:

public static boolean [] bytesToBooleansAnotherWay(byte [] bytes){
    boolean [] bools = new boolean[bytes.length * 8];

    for(int i = 0; i < bytes.length; i++){
        int j = i * 8;
        bools[j] = (bytes[i] & 0x80) != 0;
        bools[j + 1] = (bytes[i] & 0x40) != 0;
        bools[j + 2] = (bytes[i] & 0x20) != 0;
        bools[j + 3] = (bytes[i] & 0x10) != 0;
        bools[j + 4] = (bytes[i] & 0x8) != 0;
        bools[j + 5] = (bytes[i] & 0x4) != 0;
        bools[j + 6] = (bytes[i] & 0x2) != 0;
        bools[j + 7] = (bytes[i] & 0x1) != 0;
    }

    return bools;
}
brb tea
  • 345
  • 1
  • 12
0

Converting from bytes to booleans:

private static boolean[] convertToBooleanArray(byte[] bytes) {
    boolean [] result = new boolean[bytes.length * 8];

    for (int i=0; i<bytes.length; i++) {
        int index = i*8;
        result[index+0] = (bytes[i] & 0x80) != 0;
        result[index+1] = (bytes[i] & 0x40) != 0;
        result[index+2] = (bytes[i] & 0x20) != 0;
        result[index+3] = (bytes[i] & 0x10) != 0;
        result[index+4] = (bytes[i] & 0x8) != 0;
        result[index+5] = (bytes[i] & 0x4) != 0;
        result[index+6] = (bytes[i] & 0x2) != 0;
        result[index+7] = (bytes[i] & 0x1) != 0;
    }

    return result;
}

Converting from booleans to bytes by cutting off the last few booleans:

/**
 * This will round down to the nearest number of bytes.  So it will chop off the last few booleans.
 * Eg: If there are 9 booleans, then that will be 1 byte, and it will lose the last boolean.
 */
private static byte[] convertToByteArray(boolean[] booleans) {
    byte[] result = new byte[booleans.length/8];

    for (int i=0; i<result.length; i++) {
        int index = i*8;
        byte b = (byte)(
                (booleans[index+0] ? 1<<7 : 0) +
                (booleans[index+1] ? 1<<6 : 0) +
                (booleans[index+2] ? 1<<5 : 0) + 
                (booleans[index+3] ? 1<<4 : 0) +
                (booleans[index+4] ? 1<<3 : 0) +
                (booleans[index+5] ? 1<<2 : 0) + 
                (booleans[index+6] ? 1<<1 : 0) +
                (booleans[index+7] ? 1 : 0));
        result[i] = b;
    }

    return result;
}

Converting from booleans to bytes by padding the last few booleans:

/**
 * This will pad to the nearest number of bytes.  So the last few booleans will be set to the padValue.
 * Eg: If there are 9 booleans, then the last 7 will be added as the padValue (making 16 booleans).
 * 
 * @param booleans
 * @param padValue
 * @return
 */
public static byte[] convertToByteArray(boolean[] booleans, boolean padValue) {
    boolean[] paddedBooleans;
    int remainder = booleans.length % 8;

    // Booleans are already divisible by 8, nothing to pad
    if (remainder == 0) {
        paddedBooleans = booleans;
    }
    // Boolean are not divisible by 8, need to pad
    else {
        int padAmount = 8 - remainder;
        paddedBooleans = Arrays.copyOf(booleans, booleans.length + padAmount);

        for (int i=booleans.length; i<paddedBooleans.length; i++) {
            paddedBooleans[i] = padValue;
        }
    }

    // Convert the boolean array into a byte array
    byte[] result = new byte[paddedBooleans.length/8];

    for (int i=0; i<result.length; i++) {
        int index = i*8;
        byte b = (byte)(
                (paddedBooleans[index+0] ? 1<<7 : 0) +
                (paddedBooleans[index+1] ? 1<<6 : 0) +
                (paddedBooleans[index+2] ? 1<<5 : 0) + 
                (paddedBooleans[index+3] ? 1<<4 : 0) +
                (paddedBooleans[index+4] ? 1<<3 : 0) +
                (paddedBooleans[index+5] ? 1<<2 : 0) + 
                (paddedBooleans[index+6] ? 1<<1 : 0) +
                (paddedBooleans[index+7] ? 1 : 0));
        result[i] = b;
    }

    return result;
}
Craigo
  • 3,384
  • 30
  • 22