devstory

Le Tutoriel de Java Buffer

  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 représente un conteneur avec une capacité fixe pour stocker des données primitives. Il est souvent utilisé en conjonction avec Java NIO Channel(s). Plus précisément, les données seront lues à partir du Channel dans le Buffer ou écriront des données du Buffer dans le Channel.
public abstract class Buffer extends Object
La hiérarchie des classes et interfaces liées à Java NIO Buffer:
  • ByteBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
  • CharBuffer
  • MappedByteBuffer
  • ByteOrder
La relation entre le Channel et le Buffer est similaire à la celle entre un bol et une cuillère. La cuillère peut être utilisée comme un petit récipient pour prendre le sucre du bol et elle peut également être utilisée comme un petit récipient pour mettre le sucre de l'extérieur dans le bol. Donc, la cuillère équivaut à un Buffer et le bol un Channel.
Capacity, limit, position et mark sont les 4 termes les plus importants de Java NIO Buffer, ils seront expliqués en détail ci-dessous. Dans les deux modes de lecture et d'écriture, le curseur se déplace toujours vers la droite.

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()
La méthode capacity() renvoie la capacité (capacity) de ce Buffer.
Bien que vous ne puissiez lire ou écrire que sur des éléments de l'index 0 à limit-1, si vous définissez limit = capacity, vous pouvez accéder (lecture, écriture) à tous les éléments du Buffer.

4. position()

public final int position()
Renvoyer la position actuelle du curseur. Les opérations de lecture et d'écriture déplacent le curseur à la fin du Buffer. La valeur renvoyée est toujours inférieure ou égale à la limit.
  • 0 <= mark <= position <= limit <= capacity

5. position(int newPosition)

public Buffer position(int newPosition)
Définir la nouvelle position pour le curseur
  • newPostion doit être supérieur ou égal à 0 et inférieur ou égal à la limit.
  • Si newPosition < mark donc la mark sera suprimée (discard).

6. limit()

public final int limit()
Renvoyer limit (la limite) de ce Buffer.
Buffer ne prend en charge la lecture et l'écriture qu'aux éléments de l'index 0 à limit-1, les autres éléments de l'index limit à capacity-1 sont désactivés. Cependant, vous pouvez définir limit = capacity pour avoir l'accès (lire ou écrire) à tous les éléments du Buffer.
  • 0 <= mark <= position <= limit <= capacity
Dans l'exemple ci-dessous: Un CharBuffer avec capacity = 10, et limit = 7, vous ne pouvez lire et écrire que sur les éléments de l'index de 0 à 6. En cas de violation, une exception sera lancée.
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)
Définir la nouvelle valeur limit pour ce Buffer. newLimit doit être inférieur à la capacity, sinon IllegalArgumentException sera lancée.
  • Si newLimit < position alors postion sera définie comme newLimit.
  • Si newLimit < mark alors la mark sera supprimée (discard).
Par exemple :
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()
La méthode mark() est utilisée pour marquer la position actuelle du curseur. Dans le processus de manipulation de Buffer, la position du curseur peut changer, la convocation de la méthode reset() aidera le curseur à revenir à la position précédemment marquée.
  • 0 <= mark <= position <= limit <= capacity
La mark sera supprimée (discard) dans les cas suivants :
  • Convoquer la méthode setPosition(newPosition) avec newPosition < mark.
  • Convoquer la méthode setLimit(newLimit) avec newLimit < mark.
  • Convoquer la méthode clear(), rewind() ou flip().

9. reset()

public Buffer reset()
La méthode reset() est utilisée pour renvoyer le curseur à la position précédemment marquée. (Voir la méthode mark()).
Cette méthode peut lancer une InvalidMarkException si la mark n'est pas définie ou a été supprimée (discard).
La mark sera supprimée (discard) dans les cas suivants :
  • Convoquer la méthode setPosition(newPosition) avec newPosition < mark.
  • Convoquer la méthode setLimit(newLimit) avec newLimit < mark.
  • Convoquer la méthode clear(), rewind() ou flip().
Par exemple :
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()
La méthode clear() définit position = 0; limit = capacity, supprime (discard) la mark et renvoie ce Buffer. La convocation de cette méthode n'affecte pas les données sur Buffer.
Par exemple :
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()
La méthode flip() définit limit = actuelle position, position= 0 et renvoie ce Buffer et supprime (discard) la mark. (Voir l'illustration ci-dessous).
L'exemple ci-dessous correspond à l'illustration au-dessus :
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
La méthode flip() est généralement utilisée après avoir terminé d'écrire des données dans le Buffer, ce qui permet de déplacer le curseur vers l'index 0. Prêt à lire des données utiles sur le Buffer. (Voir des illustrations et exemples ci-dessous).
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()
La méthode rewind() est utilisée pour rembobiner ce Buffer, en d'autres termes, elle définit position = 0, et supprime (discard) la mark.

13. remaining()

public final int remaining()
Renvoyer le nombre d'éléments entre position et limit-1.

14. hasRemaining()

public final boolean hasRemaining()
Renvoyer true s'il y a des éléments entre position et limit-1. Sinon, renvoyer false.

15. slice()

public abstract Buffer slice();
Renvoyer un nouveau Buffer qui est un cliché partiel de ce Buffer. Le nouveau Buffer inclut les éléments entre la position et la limit-1 de ce Buffer. La position marquée du nouveau Buffer n'est pas définie, la position du nouveau Buffer est 0. (Voir l'illustration ci-dessous).
Ces deux Buffer sont liés l'un à l'autre, les changements de données sur l'un seront visibles sur l'autre et vice versa. Les valeurs mark, position, limit, capacity de ces deux Buffer(s) sont indépendantes l'une de l'autre.
Par exemple :
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();
Renvoyer un nouveau Buffer qui est un cliché de ce Buffer.
Les données du nouveau Buffer seront celles de ce Buffer. Les modifications apportées aux données de ce Buffer seront visibles dans le nouveau Buffer, et vice versa; les valeurs de position, de limit et de mark des deux Buffer seront indépendantes. Lorsqu'ils sont nouvellement créés, deux Buffer(s) disposent de mêmes valeurs position, limit, mark.
Par exemple :
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.
Renvoyer un tableau contenant les éléments de ce Buffer si effectivement ce Buffer utilise des tableaux comme technique pour stocker les éléments. Il s'agit d'une opération facultative, qui peut ne pas être prise en charge dans la sous-classe de Buffer. Si cette méthode n'est pas prise en charge, elle lancera une UnsupportedOperationException. Vérifier si ce Buffer prend en charge les tableaux à l'aide de la méthode hasArray().
La plupart des sous-classes Buffer disponibles dans le JDK utilisent un tableau interne pour stocker les éléments.
Class
Has Array?
ByteBuffer
true
MappedByteBuffer
true
ShortBuffer
true
IntBuffer
true
FloatBuffer
true
LongBuffer
true
DoubleBuffer
true
CharBuffer
true
Par exemple :
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();
Renvoyer true si ce Buffer utilise des tableaux comme technique pour stocker des éléments, sinon renvoie false. Il s'agit d'une opération facultative, qui peut ne pas être prise en charge dans la sous-classe de Buffer.
La plupart des sous-classes Buffer disponibles dans le JDK utilisent un tableau interne pour stocker les éléments.
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();
Vérifier si ce Buffer est en lecture seule (read-only) ou non. Il s'agit d'une opération facultative, qui peut ne pas être prise en charge dans la sous-classe de Buffer et une UnsupportedOperationException sera levée.
La plupart des sous-classes Buffer disponibles dans le JDK prennent en charge les modes de lecture et d'écriture (par défaut) :
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
Par exemple :
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
Par exemple : Créer un CharBuffer lecture seule:
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)
Par exemple : Créer les autres Buffer(s) lecture seule : 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();