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

Integer values

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;

Floating-point values

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!)

Byte arrays

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.

Wrapper buffers

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.

Byte order

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.