o7planning

Java Buffer Tutorial with Examples

  1. Buffer
  2. Buffer Methods
  3. capacity()
  4. position()
  5. position(int newPosition)
  6. limit()
  7. limit(int newLimit)
  8. mark()
  9. reset()
  10. clear()
  11. flip()
  12. rewind()
  13. remaining()
  14. hasRemaining()
  15. slice()
  16. duplicate()
  17. array()
  18. hasArray()
  19. arrayOffset()
  20. isReadOnly()
  21. isDirect()

1. Buffer

Java NIO Buffer represents a container with a fixed capacity to store primitive data. It is often used in conjunction with the Java NIO Channel(s). Specifically, data will be read from the Channel into the Buffer or write data from the Buffer into the Channel.
public abstract class Buffer extends Object
Hierarchy of classes and interfaces related to Java NIO Buffer:
  • ByteBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
  • CharBuffer
  • MappedByteBuffer
  • ByteOrder
The relationship between Channel and Buffer is similar to the relationship between a bowl and a spoon. The spoon can be used as a small container to take the sugar from the bowl and it can also be used as a small container to put the sugar from the outside into the bowl. Thus, the spoon acts as a Buffer and the bowl acts as a Channel.
Capacity, limit, position and mark are the 4 most important terms of Java NIO Buffer, they will be explained in detail below. In both read and write modes, the cursor always moves to the right.

2. Buffer Methods

public final int capacity()
 
public final int position()   
public Buffer position(int newPosition)  

public final int limit()   
public Buffer limit(int newLimit)

public Buffer mark()
public Buffer reset()  
public Buffer clear()  
public Buffer flip()   
public Buffer rewind()  

public final int remaining()
public final boolean hasRemaining()

public abstract boolean isReadOnly();
public abstract boolean hasArray();
public abstract Object array();
public abstract int arrayOffset();
public abstract boolean isDirect();
public abstract Buffer slice();
public abstract Buffer duplicate();

3. capacity()

public final int capacity()
The capacity() method returns the capacity of this Buffer.
Although you can only read or write to elements from index 0 to limit-1, if you set limit = capacity, you can access (read, write) to all the elements of the Buffer.

4. position()

public final int position()
Return the current position of the cursor. Both read and write operations move the cursor to the end of the Buffer. The returned value is always less than or equal to limit.
  • 0 <= mark <= position <= limit <= capacity

5. position(int newPosition)

public Buffer position(int newPosition)
Set the new position for the pointer.
  • newPostion must be greater than or equal to 0 and less than or equal to limit.
  • If newPosition < mark then the mark will be discarded.

6. limit()

public final int limit()
Returns the limit of this Buffer.
Buffer only supports reading and writing to elements at index 0 to limit-1, elements at index limit to capacity-1 are disabled. However, you can set limit = capacity to be able to access (read or write) to all the elements of the Buffer.
  • 0 <= mark <= position <= limit <= capacity
In the example below: A CharBuffer with capacity = 10, and limit = 7, you can only read from and write to elements at index from 0 to 6. Violation of an exception will be thrown.
Buffer_limit_ex1.java
// Allocate a character type buffer.
CharBuffer buffer = CharBuffer.allocate(10); // capacity = 10

buffer.limit(7); // limit = 7

String text = "abcdefghij";
System.out.println("Input text: " + text);
System.out.println("Text length: " + text.length()); // 10

for (int i = 0; i < text.length(); i++) {
    char chr = text.charAt(i);
    
    // put character in buffer.
    buffer.put(chr);
    System.out.println(i + ". put: " + chr);
}
Output:
Input text: abcdefghij
Text length: 10
0. put: a
1. put: b
2. put: c
3. put: d
4. put: e
5. put: f
6. put: g
Exception in thread "main" java.nio.BufferOverflowException
    at java.base/java.nio.Buffer.nextPutIndex(Buffer.java:665)
    at java.base/java.nio.HeapCharBuffer.put(HeapCharBuffer.java:199)
    at org.o7planning.buffer.ex.Buffer_limit_ex1.main(Buffer_limit_ex1.java:21)

7. limit(int newLimit)

public Buffer limit(int newLimit)
Set the new limit value for this Buffer. newLimit must be less than capacity, otherwise IllegalArgumentException will be thrown.
  • If newLimit < position, the postion will be set to newLimit.
  • If newLimit < mark then the mark will be discarded.
Example:
Buffer_limit_newLimit_ex1.java
// Allocate a character type buffer.
CharBuffer buffer = CharBuffer.allocate(10); // capacity = 10

System.out.printf("Buffer capacity: %d%n%n", buffer.capacity()); // 10

buffer.limit(9); // limit = 9
System.out.printf("Buffer limit: %d, position: %d%n%n", buffer.limit(), buffer.position());

System.out.println("Set newPostion: 8");
buffer.position(8);

System.out.printf("Buffer limit: %d, position: %d%n%n", buffer.limit(), buffer.position());

System.out.println("Set newLimit: 7");
// Set limit = 7.
buffer.limit(7);

System.out.printf("Buffer limit: %d, position: %d%n", buffer.limit(), buffer.position());
Output:
Buffer capacity: 10

Buffer limit: 9, position: 0

Set newPostion: 8
Buffer limit: 9, position: 8

Set newLimit: 7
Buffer limit: 7, position: 7

8. mark()

public Buffer mark()
The mark() method is used to mark the current position of the cursor. In the process of manipulating Buffer, the position of the cursor may change, calling the reset() method will help the cursor return to the previously marked position.
  • 0 <= mark <= position <= limit <= capacity
The mark will be discarded in the following cases:
  • Call the setPosition(newPosition) method with newPosition < mark.
  • Call the setLimit(newLimit) method with newLimit < mark.
  • Call the clear(), rewind() or flip() method.

9. reset()

public Buffer reset()
The reset() method is used to return the cursor to the previously marked position. (See the mark() method).
This method can throw an InvalidMarkException if the mark is not defined or has been discarded.
The mark will be discarded in the following cases:
  • Call the setPosition(newPosition) method with newPosition < mark.
  • Call the setLimit(newLimit) method with newLimit < mark.
  • Call the clear(), rewind() or flip() method.
Example:
Buffer_reset_ex1.java
CharBuffer buffer = CharBuffer.allocate(10); // capacity = 10

System.out.println("Set newPostion: 5");
buffer.position(5);

System.out.println("Mark current position!");
buffer.mark(); // marked position = 5

System.out.println("Call buffer.get() twice!");
char ch1 = buffer.get();
char ch2 = buffer.get();

System.out.printf("Position: %d%n%n", buffer.position()); // position = 7

System.out.println("Reset!");
buffer.reset();

System.out.printf("Position: %d%n%n", buffer.position()); // position = 5
Output:
Set newPostion: 5
Mark current position!
Call buffer.get() twice!
Position: 7

Reset!
Position: 5

10. clear()

public Buffer clear()
The clear() method sets position = 0; limit = capacity, discard the mark and return this Buffer. Calling this method does not affect the data on the Buffer.
Example:
Buffer_clear_ex1.java
CharBuffer buffer = CharBuffer.allocate(7); // capacity = 7

// Write data to buffer:
buffer.put('A');
buffer.put('B');

buffer.position(3); // Set position to 3.
buffer.limit(5); // Set limit to 5.

System.out.printf("buffer, capcity: %d, limit: %d, position: %d%n%n", //
        buffer.capacity(), buffer.limit(), buffer.position());

System.out.println("Clear...");
buffer.clear();

System.out.printf("buffer, capcity: %d, limit: %d, position: %d%n%n", //
        buffer.capacity(), buffer.limit(), buffer.position());

// Read data in buffer:
while (buffer.hasRemaining()) {
    char chr = buffer.get();
    System.out.println(chr + " --> " + (int) chr); // char and code.
}
Output:
buffer, capcity: 7, limit: 5, position: 3

Clear...
buffer, capcity: 7, limit: 7, position: 0

A --> 65
B --> 66
--> 0
--> 0
--> 0
--> 0
--> 0

11. flip()

public Buffer flip()
The flip() method will set limit = current position, position = 0 and return this Buffer, and discard the mark. (See illustration below).
The example below corresponds to the illustration above:
Buffer_flip_ex1.java
CharBuffer buffer = CharBuffer.allocate(10); // capacity = 10

System.out.printf("Position: %d, Limit: %d, Capacity: %d%n%n",
        buffer.position(), buffer.limit(), buffer.capacity());

System.out.println("Write 3 characters to buffer\n");
for(char ch : new char[] {'A','B','C'}) {
    buffer.put(ch);
}
System.out.printf("Position: %d, Limit: %d, Capacity: %d%n%n",
        buffer.position(), buffer.limit(), buffer.capacity());

System.out.println("Set limit = 7, position = 5\n");
buffer.limit(7);
buffer.position(5);

System.out.printf("Position: %d, Limit: %d, Capacity: %d%n%n",
        buffer.position(), buffer.limit(), buffer.capacity());   

System.out.println(" --- flip() --- \n");
buffer.flip();  

System.out.printf("Position: %d, Limit: %d, Capacity: %d",
        buffer.position(), buffer.limit(), buffer.capacity());
Output:
Position: 0, Limit: 10, Capacity: 10

Write 3 characters to buffer

Position: 3, Limit: 10, Capacity: 10

Set limit = 7, position = 5

Position: 5, Limit: 7, Capacity: 10

 --- flip() ---

Position: 0, Limit: 5, Capacity: 10
The flip() method is usually used after finishing writing data to the Buffer, which helps move the cursor to index 0. Ready to read out useful data on Buffer. (See illustrations and examples below).
Buffer_flip_ex2.java
CharBuffer buffer = CharBuffer.allocate(5); // capacity = 5  

// WRITE MODE:
System.out.println("Write 3 characters to buffer\n");
for(char ch : new char[] {'A','B','C'}) {
    buffer.put(ch);
}  
// (Now position = 3, limit = 5, capacity = 5).
System.out.println(" --- flip() --- \n");
buffer.flip();

// READ MODE:
// (Now position = 0, limit = 3, capacity = 5).
while(buffer.position() < buffer.limit()) {
   char ch = buffer.get();
   System.out.println(ch);
}
Output:
Write 3 characters to buffer

 --- flip() ---

A
B
C

12. rewind()

public Buffer rewind()
The rewind() method is used to rewind this Buffer, in other words, it sets position = 0, and discard the mark.

13. remaining()

public final int remaining()
Returns the number of elements between position and limit-1.

14. hasRemaining()

public final boolean hasRemaining()
Returns true if there are any elements between position and limit-1. Otherwise, return false.

15. slice()

public abstract Buffer slice();
Returns a new Buffer that is a partial snapshot of this Buffer. The new Buffer includes the elements between the position and limit-1 of this Buffer. The marked position of the new Buffer is not defined, the position of the new Buffer is 0. (See illustration below).
These two Buffer(s) are related to each other, data changes on one will be seen on the other and vice versa. mark, position, limit, capacity values of these two Buffer(s) are independent of each other.
Example:
Buffer_slice_ex1.java
CharBuffer buffer1 = CharBuffer.allocate(10); // capacity = 10  

// Write data to buffer1:
buffer1.put('A');
buffer1.put('B');
buffer1.put('C');

buffer1.position(1); // Set position to 1.
buffer1.limit(7); // Set limit to 7.

CharBuffer buffer2 = buffer1.slice();

System.out.printf("buffer2, capcity: %d, limit: %d, position: %d%n%n",
              buffer2.capacity(), buffer2.limit(), buffer2.position());

// Change data in buffer2:
buffer2.put('D');
buffer2.put('E');
buffer2.put('F');

//
buffer1.position(0);
buffer1.limit(4);
// Read data in buffer1:
while(buffer1.hasRemaining()) {
    System.out.println(buffer1.get());
}
Output:
buffer2, capcity: 6, limit: 6, position: 0

A
D
E
F

16. duplicate()

public abstract Buffer duplicate();
Returns a new Buffer that is a snapshot of this Buffer.
The data of the new Buffer will be that of this Buffer. Changes to this Buffer's data will be visible in the new Buffer, and vice versa; the two Buffer(s)' position, limit, and mark values will be independent. When newly created, two Buffer(s) has the same values position, limit, mark.
Example:
Buffer_duplicate_ex1.java
CharBuffer buffer1 = CharBuffer.allocate(10); // capacity = 10  

// Write data to buffer1:
buffer1.put('A');
buffer1.put('B');
buffer1.put('C');

buffer1.position(1); // Set position to 1.
buffer1.limit(7); // Set limit to 7.

CharBuffer buffer2 = buffer1.duplicate();

System.out.printf("buffer2, capcity: %d, limit: %d, position: %d%n%n",
              buffer2.capacity(), buffer2.limit(), buffer2.position());

// Change data in buffer2:
buffer2.put('D');
buffer2.put('E');
buffer2.put('F');

//
buffer1.position(0);
buffer1.limit(4);
// Read data in buffer1:
while(buffer1.hasRemaining()) {
    System.out.println(buffer1.get());
}
Output:
buffer2, capcity: 10, limit: 7, position: 1

A
D
E
F

17. array()

public abstract Object array(); // Optional Operation.
Returns an array contain the elements of this Buffer if indeed this Buffer uses arrays as a technique to store the elements. This is an optional operation, which may not be supported in the subclass of Buffer. If this method is not supported, it will throw an UnsupportedOperationException. Check if this Buffer supports arrays using the hasArray() method..
Most of the Buffer subclasses available in the JDK use an internal array to store the elements.
Class
Has Array?
ByteBuffer
true
MappedByteBuffer
true
ShortBuffer
true
IntBuffer
true
FloatBuffer
true
LongBuffer
true
DoubleBuffer
true
CharBuffer
true
Example:
Buffer_array_ex1.java
CharBuffer charBuffer = CharBuffer.allocate(5); // capacity = 5

// Write data to charBuffer:
charBuffer.put('A');
charBuffer.put('B');
charBuffer.put('C');

boolean hasArray = charBuffer.hasArray(); // true

if(hasArray)  {
    char[] charArray = charBuffer.array();
    System.out.println("charArray.length: " + charArray.length);  // 5
    for(char ch: charArray)  {
        System.out.println(ch + " --> " + (int)ch);    // char and code
    }
}
Output:
charArray.length: 5
A --> 65
B --> 66
C --> 67
--> 0
--> 0

18. hasArray()

public abstract boolean hasArray();
Returns true if this Buffer uses arrays as a technique to store elements, otherwise return false. This is an optional operation, which may not be supported in the subclass of Buffer.
Most of the Buffer subclasses available in the JDK use an internal array to store the elements.
Class
Has Array?
ByteBuffer
true
MappedByteBuffer
true
ShortBuffer
true
IntBuffer
true
FloatBuffer
true
LongBuffer
true
DoubleBuffer
true
CharBuffer
true

19. arrayOffset()

public abstract int arrayOffset();

20. isReadOnly()

public abstract boolean isReadOnly();
Checks whether this Buffer is read-only or not. This is an optional operation, which may not be supported in the subclass of Buffer and an UnsupportedOperationException will be thrown.
Most of the Buffer subclasses available in the JDK support both read and write modes (by default):
Class
Read-Only
by default?
Support
Read-Only?
ByteBuffer
false
true
MappedByteBuffer
false
true
ShortBuffer
false
true
IntBuffer
false
true
FloatBuffer
false
true
LongBuffer
false
true
DoubleBuffer
false
true
CharBuffer
false
true
Example:
Buffer_isReadOnly_ex1.java
Buffer b1 = ByteBuffer.allocate(10);
Buffer b2 = MappedByteBuffer.allocate(10);
Buffer b3 = ShortBuffer.allocate(10);
Buffer b4 = IntBuffer.allocate(10);
Buffer b5 = FloatBuffer.allocate(10);
Buffer b6 = LongBuffer.allocate(10);
Buffer b7 = DoubleBuffer.allocate(10);
Buffer b8 = CharBuffer.allocate(10);

Buffer[] buffers = new Buffer[] { b1, b2, b3, b4, b5, b6, b7, b8 };

for (Buffer buffer : buffers) {
    System.out.println(buffer.getClass().getSimpleName() + " --> " + buffer.isReadOnly());
}
Output:
HeapByteBuffer --> false
HeapByteBuffer --> false
HeapShortBuffer --> false
HeapIntBuffer --> false
HeapFloatBuffer --> false
HeapLongBuffer --> false
HeapDoubleBuffer --> false
HeapCharBuffer --> false
Example: Create a read-only CharBuffer:
Buffer_isReadOnly_ex2.java
CharBuffer charBuffer = CharBuffer.allocate(10); // capacity = 10
// Write data to charBuffer.
charBuffer.put('A');
charBuffer.put('B');
charBuffer.put('C');

// Create a read-only CharBuffer.
CharBuffer readOnlyBuffer = charBuffer.asReadOnlyBuffer();

System.out.println("Write data to read-only buffer:");
readOnlyBuffer.put('D'); // ==> java.nio.ReadOnlyBufferException
Output:
Write data to read-only buffer:
Exception in thread "main" java.nio.ReadOnlyBufferException
    at java.base/java.nio.HeapCharBufferR.put(HeapCharBufferR.java:202)
    at org.o7planning.buffer.ex.Buffer_isReadOnly_ex2.main(Buffer_isReadOnly_ex2.java:18)
Example: Create other read-only Buffer(s): ByteBuffer, MappedByteBuffer, ShortBuffer, ...
Buffer_isReadOnly_ex3.java
package org.o7planning.buffer.ex;

import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.MappedByteBuffer;
import java.nio.ShortBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class Buffer_isReadOnly_ex3 {

    public static void main(String[] args) throws IOException {
        ByteBuffer b1 = ByteBuffer.allocate(10);
        
        //
        Path pathToWrite = Paths.get("/Volumes/Data/test/out-file.txt");
        FileChannel fileChannel = (FileChannel) Files.newByteChannel(pathToWrite, //
                                                StandardOpenOption.READ,
                                                StandardOpenOption.WRITE,
                                                StandardOpenOption.TRUNCATE_EXISTING);
        
        CharBuffer charBuffer = CharBuffer.wrap("This will be written to the file");
        MappedByteBuffer b2 = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, charBuffer.length());
        //
        ShortBuffer b3 = ShortBuffer.allocate(10);
        IntBuffer b4 = IntBuffer.allocate(10);
        FloatBuffer b5 = FloatBuffer.allocate(10);
        LongBuffer b6 = LongBuffer.allocate(10);
        DoubleBuffer b7 = DoubleBuffer.allocate(10);
        CharBuffer b8 = CharBuffer.allocate(10);
        
        Buffer[] buffers = new Buffer[] { b1, b2, b3, b4, b5, b6, b7, b8 };
        for (Buffer buffer : buffers) {
            System.out.println(buffer.getClass().getSimpleName() + " --> " + buffer.isReadOnly());
        }
        System.out.println(" --------- ");
        
        ByteBuffer b1r = b1.asReadOnlyBuffer();
        MappedByteBuffer b2r = (MappedByteBuffer) b2.asReadOnlyBuffer();
        ShortBuffer b3r = b3.asReadOnlyBuffer();
        IntBuffer b4r = b4.asReadOnlyBuffer();
        FloatBuffer b5r = b5.asReadOnlyBuffer();
        LongBuffer b6r = b6.asReadOnlyBuffer();
        DoubleBuffer b7r = b7.asReadOnlyBuffer();
        CharBuffer b8r = b8.asReadOnlyBuffer();
        
        Buffer[] readOnlyBuffers = new Buffer[] { b1r, b2r, b3r, b4r, b5r, b6r, b7r, b8r };
        
        for (Buffer buffer : readOnlyBuffers) {
            System.out.println(buffer.getClass().getSimpleName() + " --> " + buffer.isReadOnly());
        }
    }
}
Output:
HeapByteBuffer --> false
DirectByteBuffer --> false
HeapShortBuffer --> false
HeapIntBuffer --> false
HeapFloatBuffer --> false
HeapLongBuffer --> false
HeapDoubleBuffer --> false
HeapCharBuffer --> false
 ---------
HeapByteBufferR --> true
DirectByteBufferR --> true
HeapShortBufferR --> true
HeapIntBufferR --> true
HeapFloatBufferR --> true
HeapLongBufferR --> true
HeapDoubleBufferR --> true
HeapCharBufferR --> true

21. isDirect()

public abstract boolean isDirect();