I have this Java class for building bit strings. The set of methods it provides resembles that of java.lang.StringBuilder.See what I have:BitStringBuilder.java:package net.coderodde.util;import java.util.BitSet;/** * This class implements a simple bit string builder. *  * @author Rodion rodde Efremov * @version 1.6 (May 25, 2016) */public class BitStringBuilder {    /**     * The minimum number of bits to commit in the constructor.     */    private static final int MINIMUM_BIT_COMMIT = 64;    private static final int BITS_PER_WORD = 64;    /**     * The actual bit storage.     */    private long[] bits;    /**     * The size of bits this builder holds.     */    private int size;    /**     * Constructs a new empty bit string builder.     */    public BitStringBuilder() {        this(MINIMUM_BIT_COMMIT);    }    /**     * Constructs a new empty bit string builder capable holding      * {@code bitCommit} bits immediately after construction.     *      * @param bitCommit the number of bits to reserve.     */    public BitStringBuilder(int bitCommit) {        bitCommit = Math.max(bitCommit, MINIMUM_BIT_COMMIT);        final int longs = bitCommit / BITS_PER_WORD +                         (bitCommit % BITS_PER_WORD != 0 ? 1 : 0);        this.bits = new long[longs];    }    public int size() {        return this.size;    }    public boolean isEmpty() {        return this.size == 0;    }    /**     * Appends the bit string described by {@code bitset} to the end of this      * builder.     *      * @param bitset  the {@link java.util.BitSet} holding the bits.     * @param numBits the number of bits to consider in {@code bitset}.     */    public void append(final BitSet bitset, final int numBits) {        if (numBits <= 0) {            return;        }        final int startIndex = this.size;        expandBitArray(this.size += numBits);        for (int i = 0, j = startIndex; i < numBits; ++i, ++j) {            writeBit(j, bitset.get(i));        }    }    /**     * Deletes all the bits whose index is at least {@code fromIndex} and at      * most {@code toIndex - 1}.     *      * @param fromIndex the starting inclusive index of the range to remove.     * @param toIndex   the ending exclusive index of the range to remove.     */    public void delete(final int fromIndex, final int toIndex) {        if (fromIndex > toIndex) {            throw new IllegalArgumentException(            'fromIndex' ( + fromIndex + ) is larger than 'toIndex' ( +            toIndex + )!);        }        if (fromIndex < 0) {            throw new IndexOutOfBoundsException(            'fromIndex' ( + fromIndex + ) is negative!);        }        if (toIndex > this.size) {            throw new IndexOutOfBoundsException(            'toIndex' ( + toIndex + ) is too large. Should be at most  +            this.size + .);        }        final int length = toIndex - fromIndex;        for (int bitIndex = toIndex; bitIndex < this.size; ++bitIndex) {            writeBit(bitIndex - length, readBit(bitIndex));        }        this.size -= length;    }    /**     * Inserts the {@code numBits} bits from {@code bitset} starting at index     * {@code startIndex}, shifting the tail elements to the right in order to      * make space for new bits.     *      * @param bitset     the {@link java.util.BitSet} holding the bits to      *                   insert.     * @param startIndex the index at which to perform the insertion.     * @param numBits    the number of bits to take from {@code bitset}.     */    public void insert(final BitSet bitset,                        final int startIndex,                        final int numBits) {        if (startIndex < 0) {            throw new IndexOutOfBoundsException(            'startIndex' ( + startIndex + ) is negative!);        }        if (startIndex > this.size) {            throw new IndexOutOfBoundsException(            'startIndex' ( + startIndex + ) is too large! Must be at most  +            this.size + .);        }        if (numBits <= 0) {            return;        }        final int oldSize = this.size;        expandBitArray(this.size += numBits);        // Shift the bit content to the right.        for (int i = oldSize - 1; i >= startIndex; --i) {            writeBit(i + numBits, readBit(i));        }        // Insert the bits from the bit set.        for (int i = 0, j = startIndex; i < numBits; ++i, ++j) {            writeBit(j, bitset.get(i));        }    }    /**     * Sets {@code numBits} bits from {@code bitset} to this builder. This      * method may extend the size of this builder from the tail.     *      * @param bitset     the {@link java.util.BitSet} holding the bits.     * @param startIndex the index at which to set the bits.     * @param numBits    the number of bits to set.     */    public void set(final BitSet bitset,                     final int startIndex,                     final int numBits) {        final int lastIndex = startIndex + numBits;        expandBitArray(lastIndex);        this.size = Math.max(this.size, lastIndex);        for (int i = startIndex, j = 0; j < numBits; ++j, ++i) {            writeBit(i, bitset.get(j));        }    }    /**     * Clears this builder.     */    public void clear() {        this.size = 0;    }    /**     * Returns the textual representation of the contents of this builder.     *      * @return the textual representation of the contents of this bit string      *         builder.     */    @Override    public String toString() {        final StringBuilder sb = new StringBuilder(this.size);        for (int i = this.size - 1; i >= 0; --i) {            sb.append(readBit(i) ? '1' : '0');        }        return sb.toString();    }    public boolean readBit(final int index) {        checkAccessIndex(index);        return readBit(index / BITS_PER_WORD, index % BITS_PER_WORD);    }    public void writeBit(final int index, final boolean value) {        checkAccessIndex(index);        writeBit(index / BITS_PER_WORD, index % BITS_PER_WORD, value);    }    public BitSet toBitSet() {        final BitSet bs = new BitSet(size);        for (int i = 0; i < this.size; ++i) {            bs.set(i, readBit(i));        }        return bs;    }    private void checkAccessIndex(final int index) {        if (this.size == 0) {            throw new IllegalStateException(The bit string builder is empty.);        }        if (index < 0) {            throw new IndexOutOfBoundsException(            The index is negative:  + index + .);        }        if (index >= this.size) {            throw new IndexOutOfBoundsException(            The index is too large ( + index + ). Must be at most  +             (this.size - 1) + !);        }    }    private boolean readBit(final int wordIndex,                            final int bitIndex) {        final long mask = 1L << bitIndex;        return (this.bits[wordIndex] & mask) != 0;    }    private void writeBit(final int wordIndex,                           final int bitIndex,                           final boolean value) {        if (value == true) {            final long mask = 1L << bitIndex;            bits[wordIndex] |= mask;        } else {            final long mask = ~(1L << bitIndex);            bits[wordIndex] &= mask;        }    }    private void expandBitArray(final int requestedCapacity) {        final int currentCapacity = this.bits.length * BITS_PER_WORD;        if (requestedCapacity <= currentCapacity) {            return;        }        final long[] newBitArray = new long[Math.max(2 * currentCapacity,                                                      requestedCapacity)];        System.arraycopy(bits, 0, newBitArray, 0, bits.length);        bits = newBitArray;    }}BitStringBuilderTest.java:    package net.coderodde.util;import java.util.BitSet;import org.junit.Test;import static org.junit.Assert.*;import org.junit.Before;public class BitStringBuilderTest {    private BitStringBuilder builder;    @Before    public void beforeEachTest() {        builder = new BitStringBuilder();    }    @Test    public void testSize() {        assertEquals(0, builder.size());        builder.append(create(101L, 3), 3);        assertEquals(3, builder.size());        builder.append(create(11L, 2), 2);        assertEquals(5, builder.size());        builder.append(create(31L, 65), 65);        assertEquals(70, builder.size());        builder.delete(10, 15);        assertEquals(65, builder.size());        builder.insert(create(1101L, 4), 10, 4);        assertEquals(69, builder.size());        builder.set(create(1001L, 5), 20, 5);        assertEquals(69, builder.size());        builder.set(create(100011L, 6), 66, 6);        assertEquals(72, builder.size());    }    @Test    public void testAppend() {        builder.append(create(0b111001L, 6), 6);        assertTrue(builder.readBit(0));        assertFalse(builder.readBit(1));        assertFalse(builder.readBit(2));        assertTrue(builder.readBit(3));        assertTrue(builder.readBit(4));        assertTrue(builder.readBit(5));        builder.append(create(0b1101L, 4), 4);        assertTrue(builder.readBit(6));        assertFalse(builder.readBit(7));        assertTrue(builder.readBit(8));        assertTrue(builder.readBit(9));    }    @Test    public void testDelete() {        builder.append(create(0b10011100010, 11), 11);        builder.delete(2, 6);        // Here 'builder' should be 1001110        assertFalse(builder.readBit(0));        assertTrue(builder.readBit(1));        assertTrue(builder.readBit(2));        assertTrue(builder.readBit(3));        assertFalse(builder.readBit(4));        assertFalse(builder.readBit(5));        assertTrue(builder.readBit(6));    }    @Test    public void testInsert() {        builder.insert(create(0b1011L, 4), 0, 4);        assertTrue(builder.readBit(0));        assertTrue(builder.readBit(1));        assertFalse(builder.readBit(2));        assertTrue(builder.readBit(3));        builder.insert(create(0b00L, 2), 2, 2);        // Here 'builder' should be 100011        assertEquals(100011, builder.toString());    }    @Test    public void testSet() {        builder.append(create(0b10_0110_0010L, 10), 10);        builder.set(create(0b110, 3), 2, 3);        assertEquals(1001111010, builder.toString());        builder.set(create(0b100L, 3), 8, 3);        assertEquals(10001111010, builder.toString());    }    @Test    public void testToString() {        builder.append(create(0b1011011100L, 10), 10);        assertEquals(1011011100, builder.toString());    }    @Test    public void testReadBit() {        builder.append(create(0b10110L, 5), 5);        assertFalse(builder.readBit(0));        assertTrue(builder.readBit(1));        assertTrue(builder.readBit(2));        assertFalse(builder.readBit(3));        assertTrue(builder.readBit(4));    }    @Test    public void testWriteBit() {        builder.append(create(0b10110L, 5), 5);        builder.writeBit(2, false);        builder.writeBit(0, true);        assertTrue(builder.readBit(0));        assertTrue(builder.readBit(1));        assertFalse(builder.readBit(2));        assertFalse(builder.readBit(3));        assertTrue(builder.readBit(4));    }    @Test    public void testToBitSet() {        assertTrue(builder.isEmpty());        builder.append(create(0b011100L, 6), 6);        final BitSet bs = builder.toBitSet();        assertFalse(builder.isEmpty());        assertFalse(bs.get(0));        assertFalse(bs.get(1));        assertTrue(bs.get(2));        assertTrue(bs.get(3));        assertTrue(bs.get(4));        assertFalse(bs.get(5));    }    @Test(expected = IllegalArgumentException.class)    public void testDeleteThrowsOnInversedIndices() {        builder.append(create(0b101L, 3), 3);        builder.delete(1, 0);    }    @Test(expected = IndexOutOfBoundsException.class)    public void testDeleteThrowsOnNegativeFromIndex() {        builder.append(create(0b101L, 3), 3);        builder.delete(-1, 0);    }    @Test(expected = IndexOutOfBoundsException.class)    public void testDeleteThrowsOnTooLargeToIndex() {        builder.append(create(0b101L, 3), 3);        builder.delete(1, 4);    }    @Test(expected = IndexOutOfBoundsException.class)    public void testInsertThrowsOnNegativeStartIndex() {        builder.insert(create(0b111L, 3), -1, 3);    }    @Test(expected = IndexOutOfBoundsException.class)    public void testInsertThrowsOnTooLargeIndex() {        builder.append(create(0b001100L, 6), 6);        builder.insert(create(0b11L, 2), 7, 2);    }    @Test(expected = IndexOutOfBoundsException.class)    public void testReadBitThrowsOnNegativeIndex() {        builder.append(create(0b10L, 2), 2);        builder.readBit(-1);    }    @Test(expected = IndexOutOfBoundsException.class)    public void testReadBitThrowsOnTooLargeIndex() {        builder.append(create(0b10L, 2), 2);        builder.readBit(3);    }    @Test(expected = IndexOutOfBoundsException.class)    public void testWriteBitThrowsOnNegativeIndex() {        builder.append(create(0b10L, 2), 2);        builder.writeBit(-1, false);    }    @Test(expected = IndexOutOfBoundsException.class)    public void testWriteBitThrowsOnTooLargeIndex() {        builder.append(create(0b10L, 2), 2);        builder.writeBit(3, false);    }    private BitSet create(final long bits, final int length) {        BitSet bitset = new BitSet();        for (int i = 0; i < length; ++i) {            bitset.set(i, (bits & (1L << i)) != 0);        }        return bitset;    }}Critique requestI would like to hear comments regarding API design, naming/coding conventions and testing, yet feel free to tell me anything that comes to mind.
Bit string builder for Java
java;bitwise;bitset