Reading and writing non-byte types in a ByteBuffer
Having looked at reading and writing
a single byte using the get() and put() methods, we turn our attention
to other types. For each of the Java primitive types (short, int etc), there
are two corresponding get methods and two corresponding put methods. The methods bear the
name of the primitive type (getInt(), getDouble(), putShort() etc).
And each method exists in two flavours: with and without an explicit offset. For example:
int i = bb.getInt(); // read an int at the current position
short sh = bb.getShort(); // read a short at the current position
int i2 = bb.getInt(20); // read an int at offset 20
bb.putDouble(10, 1.5d); // put the double value 1.5 at offset 10
Java has primitive data types for 1-byte, 2-byte, 4-byte and 8-byte integers. But, with the
exception of the 2-byte integers, Java integer primitives
are always signed.
With the exception of the 8-byte size, this bias towards signed integers isn't generally such a problem:
with a bit of care, we can use the "next size up".
For example, if we need to process a 4-byte integer as an unsigned value, we can store it in an 8-byte
long. Although we can't perform "true" unsigned arithmetic, provided we don't let the value overflow,
this method works for simple tasks such as reading an unsigned file size. For example, to read an unsigned
4-byte value, we can use the getInt() method, but read the value into the next size up:
a long. To transform the value read into an unsigned 4-byte value, we need to "mask off" the
upper 4 bytes of the long (which effectively contain the sign):
long unsignedInt = bb.getInt() & 0xffffffffL;
The ByteBuffer's getFloat(), getDouble(), putFloat() and putDouble()
methods read and write standard IEEE-754 format
floating-point values. (In case you don't know about IEEE-754: basically, if you have some floating-point numbers
in binary format, they're almost certainly stored in IEEE-754 format unless you know they're in something else!)
Versions of ByteBuffer.get() and ByteBuffer.put() are also provided to read and write byte
arrays. Note that these always transfer the entire array or fail with an exception.
For other types of arrays, there are type-specific buffer classes.
For more information, see the separate
page on reading and writing arrays a NIO buffer.
The methods on ByteBuffer are good for reading and writing single ints, floats.
If you want to write multiple values of these different primitive types, it's often more efficient
or convenient to create a wrapper buffer of type
IntBuffer, FloatBuffer etc by calling ByteBuffer.asIntBuffer() etc.
When multiple bytes are stuck together and treated as a single value (e.g. a 4-byte integer), there is
the issue of byte order (sometimes called byte sex or endianness).
In other words, we have to know or decide which way round to store the bytes: to we
put the byte representing the high-valued portion of the number first, or that representing the
low-valued portion? ByteBuffer and other buffer classes provide the order() method
for specifying the byte order for getting and putting values. For more details, see the
separate page on byte order.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.