The Buffer Zone

While most of the changes in Java 2 Standard Edition Version 1.4 (J2SE 1.4) are relatively minor, programmers who work with input and output received an embarrassment of riches in the form of the java.nio package.

While most of the changes in Java 2 Standard Edition Version 1.4 (J2SE 1.4) are relatively minor, programmers who work with input and output received an embarrassment of riches in the form of the java.nio package.

Java has always supported I/O programming through the java.io package, and java.io continues to be supported in J2SE 1.4. However, there’s considerable benefit to using java.nio:

  • Large amounts of primitive data can be accessed and transformed quickly using buffers.

  • Files can be read and written using channels, which support file locking, memory mapping, and character set encoding.

  • Data can be exchanged over a network more safely with secure sockets and more reliably with socket channels, the first Java networking classes that support non-blocking input and output.

This month and next, we’ll focus on java.nio, starting with buffers.


java.nio‘s buffers are objects that hold large amounts of data in memory. Each buffer holds a single kind of Java primitive (in a sense, each buffer has a “type”), and maintains its own state, including its maximum capacity and the position of the next read or write.

With the abstract Buffer class at the top of the hierarchy, there is an abstract buffer class for almost all of the primitive data types in Java: ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, and ShortBuffer. (What’s missing? A buffer for Boolean.)

For example, the following code creates an IntBuffer from an array of integers:

int[] goals =
{ 0, 1, 0, 0, 2, 0, 0, 1, 3, 0, 0 };
IntBuffer arrayBuffer =

Because all of the buffer classes are abstract, you can’t create a buffer with a constructor. Instead, each buffer class has two static methods that return buffers: allocate() and wrap().

As shown above, you can call wrap() with one argument, the array of data, to create a new buffer with as many elements as the array. A variant of wrap() creates a buffer from a portion of an array.

int[] goals =
{ 0, 1, 0, 0, 2, 0, 0, 1, 3, 0, 0 };
IntBuffer portionBuffer =
IntBuffer.wrap(goals, 4, 3);

The three arguments to this wrap() method is the array, the offset in the array to begin copying data from, and the number of elements to copy.

Note that when you create a buffer using wrap(), changes to the buffer’s data change the values of the array used to create it, and vice versa.

If you want to create an empty buffer (without specifying an initial array of values), use allocate(int n) to create a new buffer with n elements.


As mentioned above, each buffer maintains its own state. However, you can query and affect the state of the buffer via its public methods.

  • A buffer’s capacity is the maximum number of elements it can hold. Call the instance method capacity() to retrieve it. A buffer’s capacity cannot be changed after the buffer has been created.

  • A buffer’s limit is the index of the last element in the buffer that contains valid data. No data will be read or written beyond a buffer’s limit. Call a buffer’s limit() method to find its present value and limit(int n) to change it.

  • Each buffer tracks position, an index into the buffer where the next datum will be read from or written to. Call a buffer’s position() method to retrieve this value, which ranges from 0 to the buffer’s limit. To change the position, call position(int n), where n is the new position.

  • A mark is a saved position in a buffer. Call mark() to set the mark to the current position. Later, when you want to return to the mark, call reset().

The initial values of capacity, limit, and position depend on how you create the buffer. If you create a new buffer from an entire array, the buffer’s capacity and limit will be the same as the number of elements in the array, and position will be set to 0.

For example, using arrayBuffer from above, the statement

+ ” ” + arrayBuffer.capacity() + ” ” + arrayBuffer.position());

prints 11 11 0. However, if you create a buffer from a portion of an array, the buffer’s capacity will be the total number of array elements (even though only a portion of the array was used), its position will be the same as the offset position, and the limit will be the position of the last array element wrapped into the buffer. For example, the statement

+ ” ” + portionBuffer.capacity() + ” ” + portionBuffer.position());

prints 7 11 4.

There are three additional methods that manipulate buffer state: flip(), clear(), and rewind().

  • After you have filled up a buffer with data, you can call the flip() method to set the limit to the current position, move the position to the start of the buffer (0), and clear the current mark. This is useful when you have finished writing data to a buffer and you want to begin reading it.

  • When you are done with a buffer and want to fill it with new data, call the clear() method. It sets the limit to the capacity of the buffer, moves the position to 0, and clears the mark. Contrary to what you might expect from the method’s name, this does not delete any data in the buffer.

  • Call the rewind() method to clear the current mark and set the position to 0.


Buffers are useless if you can’t put things in them and take things out. As you might expect, java.nio buffers have get() methods to read data from a buffer and put() methods to store data.

The arguments for put() depend on the type of buffer you’re working with. An IntBuffer has put() methods that take an integer argument, FloatBuffer has put() methods that take a float argument, and so on. Data can be stored in character buffers (CharBuffer) using these five methods:

  • put(char c) stores character c at the current position in the buffer and then increments the position.

  • put(int n, char c) stores character c at position n in the buffer, but does not increment the position.

  • put(char[]) stores an entire character array in the buffer, beginning at the first position of the buffer, and increments the current position one element beyond the array data.

  • put(char[], int i, int j) stores a portion of a character array in the buffer. i specifies the offset in the array to begin copying data from. j specifies the number of elements from the array to copy. The buffer’s position will be incremented accordingly.

  • put(CharBuffer) stores the contents of another CharBuffer (the source) into the buffer, beginning at the current position of the source buffer and ending at its limit, incrementing the position as it goes.

These methods can run afoul of several exceptions. ReadOnlyBufferException is thrown if the buffer has been set up to be read-only. BufferOverflowException is thrown if the buffer’s position is beyond its limit. And an IndexOutOfBoundsException occurs if an array index used in a put() call is invalid.

Data can be retrieved from buffers using several different get() methods. The return type and arguments reflect the kind of primitive data the buffer holds. Here’s the methods available for retrieving data from a CharBuffer:

  • char get() returns the character at the current position and then increments the position.

  • char get(int i) returns the character at i without affecting the buffer’s position.

  • CharBuffer get(char[]) fills the specified character array with all of the elements in the buffer from the current position to the limit.

  • CharBuffer get(char[], int i, int j) fills a portion of a character array, beginning at offset i. The third argument, j specifies the number of array elements that will be filled with buffer data.


As you read from a buffer, two methods keep you from overrunning a buffer’s limit: hasRemaining() and remaining().

The remaining() method returns the number of elements you can read before hitting the buffer’s limit. It can be used as part of a for loop to read everything that’s left in a buffer from the current position to the limit. Here’s an example that reads and displays elements of a buffer called imageData:

for (int index = 0;
imageData.remaining() > 0; index++) {
System.out.print(imageData.get() + ” “);

The hasRemaining() method returns true if at least one more element can be read from a buffer before hitting the limit.

A Buffering Network Client

To put all of this together, the NioFinger class in Listing One uses buffers in an implementation of a simple finger client.

Listing One: A finger client that uses java.nio buffers

import java.io.*; import java.nio.*;
import java.net.*; import java.util.*;

public class NioFinger {
public CharBuffer plan;

public NioFinger(String target) {
StringTokenizer split = new StringTokenizer(target, “@”);
String user = split.nextToken();
String host = split.nextToken();

plan = getPlanFile(user, host);

public CharBuffer getPlanFile(String user, String host) {
// create buffer to hold plan file
CharBuffer data = CharBuffer.allocate(131072);

try {
// send finger request
Socket fServer = new Socket(host, 79);

PrintStream out = new

out.print(user + “\015\012″);
BufferedReader in = new BufferedReader(
new InputStreamReader(fServer.getInputStream()));
boolean end_of_file = false;

while (!end_of_file) {
int inChar = in.read();

if ((inChar != -1) & (data.hasRemaining()))
data.put((char) inChar);
end_of_file = true;
} catch (IOException e) {
System.out.println(“IO Error:” + e.getMessage());
return data;

public static void main(String[] arguments) {
if (arguments.length > 0) {
NioFinger digit = new NioFinger(arguments[0]);

} else
System.out.println(“Usage: java NioFinger user@host“);

The finger protocol lets Internet users request information about each other. On a system running a finger server, a user can provide information by creating a .plan file, which will be displayed by the server to anyone who requests it.

Finger has fallen into disuse in recent years because of security concerns and the popularity of the World Wide Web (which is perfect for Internet exhibitionism). However, there’s still one group that’s so old-school that they use finger: game programmers. The GameFinger Web site lists dozens of these throwbacks at http://finger.planetquake.com.

The NioFinger class makes a connection to a finger server and stores the data it gets back in a character buffer, saving it in an instance variable named plan.

NioFinger objects are created by calling the NioFinger (String) constructor with one argument, the name and location of a user. Name and location is specified in the form username@hostname, as in johnc@idsoftware.com, the finger address of id Software founder John Carmack.

The constructor method splits the address up and calls the object’s getPlanFile(String, String) method with two arguments, the target’s username and hostname, respectively. This method returns a CharBuffer that holds the data returned by the finger server.

To see an example of how NioFinger can be used, look at main(). It creates a NioFinger object and displays the contents of the object’s plan variable by calling its toString() method.

Most of the code in the NioFinger class makes use of techniques in the java.io and java.net packages for reading data through a stream over the Internet.

All of the code that uses buffers is found in the getPlanFile() method, which loads the text of a .plan file into the plan variable. Here’s a rundown of the buffering code in the method:

1. An empty character buffer is created with CharBuffer data = CharBuffer.allocate(131072);. This buffer is allocated with enough space to hold 128K characters, which ought to be enough to hold even the most verbose .plan file.

2. Integers are read one at a time over a network socket, recast as characters and stored in the buffer with the code snippet

if ((inChar != -1) & (data.hasRemaining()))
data.put((char) inChar);

3. The call to put(char) stores a character in the buffer. The buffer’s hasRemaining() method is used to ensure that the incoming finger data does not extend beyond the limit of the buffer. hasRemaining() would return false if the current position was equal or greater than the limit.

4. After all data has been read (or the limit has been reached), the buffer is flipped with data.flip().

Calling flip() sets up a buffer to be read after it has been filled with data. It sets the limit to the current position and resets the position to 0. If the data.flip() was omitted from NioBuffer class then, toString() would show all of the empty buffer elements from the end of the plan data to the 128 K capacity of the buffer.

With one exception, the buffer classes in the java.nio package all work like CharBuffer. You work with a primitive type individually and in arrays, storing and retrieving data using put() and get() methods.

Make Mine a ByteBuffer

The exception is ByteBuffer, probably the most versatile buffer class. Byte buffers have get() and put() methods to work with other data types, such as getDouble() and putFloat(float); an allocateDirect() method to create operating system-friendly buffers; and a compact() method to compact a buffer by discarding all bytes before the current position and filling the buffer with everything from the current position to the limit. There’s also a subclass called MappedByteBuffer that supports memory mapping.

Next month, we’ll use byte buffers and other java.nio features to create a finger server, assuming of course, that everything goes according to .plan.

Rogers Cadenhead is a Web application developer and the coauthor of Teach Yourself Java 2 in 21 Days, Third Edition from Sams Publishing. To contact Cadenhead, visit his weblog at http://workbench.cadenhead.info. The Java source code used in this column can be downloaded from http://www.linux-mag.com/downloads/2002-10/java.

Comments are closed.