Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert boolean array to binary and vice versa in Java?

What is the most efficient way to output a boolean array to (and input from) a file in Java? I was going to use a string with each character being either 't' or 'f' and then I thought, why not take eight time less space?

NOTE

I actually have no idea which answer is the better method, I've just chosen Peter's because I understand it. Thanks to both answerers!

like image 823
flea whale Avatar asked Dec 12 '22 05:12

flea whale


2 Answers

Say you have a boolean[]

boolean[] ar = {true,false,false,true,false,true,true,true,false,true,false,false,false,true,tr‌​ue};

and you want to write this to a disk, and you don't care how its is implemented in memory.

public static void main(String... args) throws IOException {
    boolean[] ar = {true, false, false, true, false, true, true, true, false, true, false, false, false, true, true};

    FileOutputStream out = new FileOutputStream("test.dat");
    writeBooleans(out, ar);
    out.close();

    FileInputStream in = new FileInputStream("test.dat");
    boolean[] ar2 = new boolean[ar.length]; 
    readBooleans(in, ar2);
    in.close();

    System.out.println(Arrays.toString(ar));
    System.out.println(Arrays.toString(ar2));
    System.out.println("The file size was "+new File("test.dat").length()+" bytes.");
}

private static void writeBooleans(OutputStream out, boolean[] ar) throws IOException {
    for (int i = 0; i < ar.length; i += 8) {
        int b = 0;
        for (int j = Math.min(i + 7, ar.length-1); j >= i; j--) {
            b = (b << 1) | (ar[j] ? 1 : 0);
        }
        out.write(b);
    }
}

private static void readBooleans(InputStream in, boolean[] ar) throws IOException {
    for (int i = 0; i < ar.length; i += 8) {
        int b = in.read();
        if (b < 0) throw new EOFException();
        for (int j = i; j < i + 8 && j < ar.length; j++) {
            ar[j] = (b & 1) != 0;
            b >>>= 1;
        }
    }
}

prints

[true, false, false, true, false, true, true, true, false, true, false, false, false, true, true]
[true, false, false, true, false, true, true, true, false, true, false, false, false, true, true]
The file size was 2 bytes.

but if I look at how big the file actually is

$ ls -l test.dat
-rw-rw-r-- 1 peter peter 2 2012-02-19 14:04 test.dat
$ du -h test.dat 
4.0K    test.dat

It says the length is 2 bytes, but the disk space used is actually 4 KB.

Note: About 1 minute of your time is worth about the same as 80 MB of SSD (expensive disk, more for HDD) So if you don't think you will be saving at least 80 MB by using this, you could be wasting your time. ;)


You can use BitSet, which can take 16x less space as each character is 16-bit.

like image 185
Peter Lawrey Avatar answered Jan 22 '23 23:01

Peter Lawrey


Newly created, just for you. I'll leave the BooleanInputStream as an excercise. Note that the first bit is now the rightmost (MSB) bit in the file (remove Byte.SIZE - 1 - in the example for other byte order, whatever you prefer). Simply use e.g. DataOutputStream to write the size of the thing to file first. 10K should fit in an integer.

Note that storing a boolean array of 10K elements is very inefficient memory wise, you certainly should use BitSet for that (finally, somebody who needs BitSet)!

public final class BooleanOutputStream extends FilterOutputStream {

    private int bitIndex;
    private byte buffer;

    public BooleanOutputStream(final OutputStream out) {
        super(out);
    }

    public void writeBoolean(final boolean value) throws IOException {
        buffer ^= (value ? 1 : 0) << (Byte.SIZE - 1 - bitIndex++);
        if (bitIndex == Byte.SIZE) {
            write(buffer & 0xFF);
            buffer = 0;
            bitIndex = 0;
        }
    }

    /**
     * This is an encoder and does therefore not close the underlying stream.
     * Please close underlying stream separately.
     */
    public void close() throws IOException {
        if (bitIndex != 0) {
            out.write(buffer);
            buffer = 0;
            bitIndex = 0;
        }
    }
}

public class BooleanInputStream extends FilterInputStream {

    private int bitIndex;
    private byte buffer;

    public BooleanInputStream(final InputStream in) {
        super(in);
    }

    public boolean readBoolean() throws IOException {
        if (bitIndex == 0) {
            int b = read();
            if (b == -1) {
                throw new EOFException();
            }
            buffer = (byte) b;
        }

        boolean value = (buffer & (1 << (Byte.SIZE - 1 - bitIndex++))) != 0;
        if (bitIndex == Byte.SIZE) {
            bitIndex = 0;
        }
        return value;
    }

    /**
     * This is a decoder and therefore does not close the underlying stream.
     * Please close underlying stream separately.
     */    
    public void close() throws IOException {
        buffer = 0;
        bitIndex = 0;
    }
}
like image 32
Maarten Bodewes Avatar answered Jan 22 '23 21:01

Maarten Bodewes