he classes in the java.io
packages make it easy to read and
write just about any kind of datasuch as bytes, or arrays of bytes. Other classes make it easy to read and write other
data types; however, all these classes read and write data in pieces that are at least a byte long. For example, the
DataInputStream and DataOutputStream classes can write Boolean values, which requirein theoryonly a single bit to
store. These classes, however, use an entire byte for each Boolean value.
In contrast, this article describes a pair of stream classes that let you read and write single bits easily. Not a bit stored
as a byte, but a real bit. They treat the data stream as a stream of bits, rather than a stream of bytes. You don't have to
think in terms of bytes at all.
This kind of facility is particularly useful when you are trying to save space. Some file formats store certain values using
bit lengths other than 8, 16, or 32. Otherssuch as data compression formatsbenefit from being able to forget
about byte boundaries entirely, and to treat data as a homogenous stream of bits.
How BitInputStream and BitOutputStream Work
Before getting into the nitty-gritty, here's a quick glance at how you might use these classes. In many ways, they're like
regular InputStream and OutputStream classes, except that they act on bits rather than bytes. Here's a program fragment that
writes three bits1, 1, and 0to a file.
FileOutputStream fout =
new FileOutputStream( "bits.dat" );
BitOutputStream bout = new BitOutputStream( fout );
bout.writeBit( 1 );
bout.writeBit( 1 );
bout.writeBit( 0 );
Likewise, here's some code to read them back in:
FileInputStream fin = new FileInputStream( "bits.dat" );
BitInputStream bin = new BitInputStream( fin );
int b0 = bin.readBit();
int b1 = bin.readBit();
int b2 = bin.readBit();
Before learning how they work, you should think a little bit about how bits are stored in files.
Creating a Bit Data Format
It's important to understand that BitInputStream
a special formatthey use the bits contained in traditional Java streams, except that they deal with the data one bit at
There is a flaw to this approach. File systems themselves deal with data in terms of bytes. Files must
bytesit's not possible to have a file that contains a partial byte. You can, of course, write a partial byte by padding
the byte with zeros and writing the entire byte. The problem with this approach is that when you read the file in again, you
can't distinguish between the actual data bytes and the padding. Practically speaking, this means that you can write a
certain number of bits to a filebut when you read the file back in, it may contain more bits than you originally wrote
And it's not just files: most streams are connected, in the end, to some facility that deals with bytes, not with individual
The essence of the problem is that a bit stream (a BitInputStream or BitOutputStream) is slightly more general than a byte
stream (an InputStream or OutputStream). A sequence of bits can be any length; a sequence of bytes has a length, in bits,
that is an even multiple of eight.
It's possible to create a special file format that could store extra information, such as the number of padding bits, which
would allow perfect reconstruction of a byte stream. However, that's not the primary goal. The primary goal is to be able to
view any data sourcesuch as a fileas a stream of bits.
Doing Without Stream Inheritance
If you look at the source code in Listing 1
and Listing 2
, you'll find that neither BitInputStream nor BitOutputStream are subclasses of InputStream and
OutputStream. Again, that underscores the basic difference between bit streams and byte streams. Most of the methods of InputStream
would be meaningless, or at least awkward, as part of
However, the methods that the BitInputStream
are analogous to the methods in the traditional stream classes. As you saw in the usage examples, these classes take streams
in their constructors, much like traditional stream filters:
BitOutputStream bout = new BitOutputStream( out );
Where traditional stream classes have read()
BitInputStream and BitOutputStream classes have readBit()
bout.writeBit( 1 );
And, like traditional stream methods, the classes have close()
Because they aren't subclasses of InputStream
, the bit
streams classes can't be used as simple replacements. But that's fineit doesn't really make sense to try to use bit
streams in places where you need byte streams.