ByteBuffer in Java

Summary

Java's ByteBuffer, introduced in Java 1.4, offers an efficient way to represent and access raw structured data from sources like files or networks, providing faster performance than traditional byte arrays. Developers can create buffers by wrapping existing arrays, allocating on the heap, or allocating direct buffers, with the latter optimizing native I/O by avoiding intermediate data copies. It simplifies data manipulation through dedicated get and put methods for various primitive types, abstracting the underlying byte array operations. Crucial methods like flip(), position(), and limit() manage the buffer's internal cursor, making ByteBuffer a key component for high-performance low-level I/O tasks in areas such as network communication or database systems.

ByteBuffer is introduced in java.nio since Java 1.4. It provides a way of representing raw structured data such as from a file or from network. It enables fast access of underlying data compared to traditional ways like byte[]

Prior to Java 1.4, if you want to represent a structured raw data, you need to create a byte[] and then having a set of checks to delimit the byte array to get the expected tokens.

There are three ways to create a ByteBuffer:

  1. Wrapping an exiting array by calling ByteBuffer.wrap();
  2. Creating an empty buffer with capacity by calling ByteBuffer.allocate();
  3. Creating an direct empty buffer with capacity by calling ByteBuffer.allocateDirect()

ByteBuffer.allocate() will create an underlying array to back the buffer. The memory may not be continuous on the RAM. While ByteBuffer.allocateDirect() will create a continuous memory buffer and make a best effort to perform native I/O operations directly upon it.

When performing data IO on direct buffer, JVM will attempt to avoid copying the buffer's content to (or from) an intermediate buffer before (or after) each invocation of one of the underlying operating system's native I/O operations.

This class provides a set of gets and puts methods to read and write different types of data. For example, getInt(), getChar(), getShort(), putInt(), putChar(), putShort() etc. With these methods, the structural representation of raw data becomes reality. One example :

ByteBuffer buffer = ByteBuffer.allocate(100);

buffer.putInt(10);
buffer.putChar('c');

buffer.flip();  //RESET THE BUFFER POSITION FOR READ

System.out.println("int : "+buffer.getInt());
System.out.println("char : "+buffer.getChar());

Now if we put an integer into the buffer, we can directly call buffer.getInt() to get the integer, there is no need for the developer to handle how the integer is read from the byte array internally.

There are also a set of methods to maintain or set the position of the buffer, it is similar to cursor in a database. These methods include position(), seek(), flip(), reset(), limit() etc. From the above example, to read the data we put in the buffer, the position has been reset to its original position(0), this can be done by calling buffer.flip(). Without this call, when calling buffer.getInt(), we will get a value of 0.

ByteBuffer is frequently used when we need to do fast low level I/O operations. For example, during network communication such as TCP/IP, writing a database (DBMS) or doing encryption/decryption operations.

JAVA BYTEBUFFER ALLOCATION

  RELATED

  COMMENTS

0

No comment for this article.