betacode

Руководство 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 представляет собой контейнер с фиксированной емкостью (capacity) для хранения примитивных данных. Он часто используется в сочетании с Java NIO Channel(s). В частности, данные будут считываться из Channel в Buffer или записываться из Buffer в Channel.
public abstract class Buffer extends Object
Иерархия классов и интерфейсов, связанных с Java NIO Buffer:
  • ByteBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
  • CharBuffer
  • MappedByteBuffer
  • ByteOrder
Взаимосвязь между Channel и Buffer аналогична взаимосвязи между миской и ложкой. Ложку можно использовать как небольшой контейнер для извлечения сахара из миски, а также как небольшой контейнер для добавления сахара в миску извне. Таким образом, ложка действует как Buffer, а миска - как Channel.
Capacity, limit, position и mark - это 4 наиболее важных термина of Java NIO Buffer, они будут подробно объяснены ниже. Как в режиме чтения, так и в режиме записи курсор всегда перемещается вправо.

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()
Метод capacity() возвращает емкость (capacity) этого Buffer.
Хотя вы можете читать или записывать только элементы с индексом от 0 до limit-1, если вы установите limit = capacity, вы сможете получить доступ (чтение, запись) ко всем элементам of Buffer.

4. position()

public final int position()
Возвращает текущее положение курсора. Как операции чтения, так и записи перемещают курсор в конец of Buffer. Возвращаемое значение всегда меньше или равно limit.
  • 0 <= mark <= position <= limit <= capacity

5. position(int newPosition)

public Buffer position(int newPosition)
Установите новое положение курсора.
  • newPostion должно быть больше или равно 0 и меньше или равно limit.
  • Если newPosition < mark, то mark будет отброшена (discard).

6. limit()

public final int limit()
Возвращает limit (предел) этого Buffer.
Buffer поддерживает только чтение и запись в элементы с индексом от 0 до limit-1, элементы с индексом от limit до capacity-1 отключены. Однако вы можете установить limit = capacity, чтобы иметь возможность доступа (чтения или записи) ко всем элементам буфера of Buffer.
  • 0 <= mark <= position <= limit <= capacity
В приведенном ниже примере: один CharBuffer с capacity = 10, и limit = 7, вы можете только читать и записывать в элементы с индексами от 0 до 6. Нарушение будет сгенерировано исключение.
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)
Установите новое значение limit для этого Buffer. newLimit должен быть меньше capacity, иначе будет выдано исключение IllegalArgumentException.
  • Если newLimit < position, тогда postion будет установлено как значение newLimit.
  • Если newLimit < mark, то mark будет отброшена (discard).
Например:
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()
Метод mark() используется для обозначения текущего положения курсора. В процессе манипулирования с Buffer положение курсора может измениться, вызов метода reset() поможет курсору вернуться в ранее отмеченное положение.
  • 0 <= mark <= position <= limit <= capacity
mark будет отброшена (discard) в следующих случаях:
  • Вызов метода setPosition(newPosition) с помощью newPosition < mark.
  • Вызов метода setLimit(newLimit) с newLimit < mark.
  • Вызов метода clear(), rewind() или flip().

9. reset()

public Buffer reset()
Метод reset() используется для возврата курсора в ранее отмеченную позицию. (См. Метод mark()).
Этот метод может вызвать исключение InvalidMarkException, если mark не определена или была отброшена (discard).
mark будет отброшена (discard) в следующих случаях:
  • Вызов метода setPosition(newPosition) с помощью newPosition < mark.
  • Вызов метода setLimit(newLimit) с newLimit < mark.
  • Вызов метода clear(), rewind() или flip().
Например:
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()
Метод clear() устанавливает position = 0; limit = capacity, отбрасывает (discard) mark и возвращает этот Buffer. Вызов этого метода не влияет на данные в Buffer.
Например:
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()
Метод flip() установит limit = position позиция, position = 0 и возвращает этот Buffer, а отбрасывает mark. (См. иллюстрацию ниже).
Приведенный ниже пример соответствует приведенной выше иллюстрации:
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
Метод flip() обычно используется после завершения записи данных в Buffer, что помогает переместить курсор на индекс 0, готов к считыванию полезных данных в Buffer. (См. иллюстрации и примеры ниже).
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()
Метод rewind() используется для перемотки этого Buffer, другими словами, он устанавливает position = 0 и удаляет (discard) mark.

13. remaining()

public final int remaining()
Возвращает количество элементов между position и limit-1.

14. hasRemaining()

public final boolean hasRemaining()
Возвращает значение true, если между position и limit-1 есть какие-либо элементы. В противном случае возвращает значение false.

15. slice()

public abstract Buffer slice();
Возвращает новый Buffer, являющийся частичным снимком этогоBuffer. Новый Buffer включает в себя элементы между п position и limit-1 этого Buffer. Отмеченная позиция (marked position) нового Buffer не определена, position нового Buffer равна 0. (См. иллюстрацию ниже).
Эти два Buffer связаны друг с другом, изменения данных на одном в одном Buffer повлияют на другой Buffer и наоборот. Значения mark, position, limit, capacity этих двух Buffer независимы друг от друга.
Например:
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();
Возвращает новый Buffer, который является снимком этого Buffer.
Данные нового Buffer будут соответствовать данным этого Buffer. Изменения данных этого Buffer будут видны в новом Buffer, и наоборот; значения position, limit и mark двух Buffer будут независимыми. При новом создании два Buffer имеют одинаковые значения position, limit, mark.
Например:
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.
Возвращает массив, содержащий элементы этого Buffer, если действительно этот Buffer использует массивы в качестве метода хранения элементов. Это опциональная операция (optional operation), которая может не поддерживаться в подклассе Buffer. Если этот метод не поддерживается, он вызовет исключение UnsupportedOperationException. Проверьте, поддерживает ли этот Buffer массивы, используя метод hasArray().
Большинство подклассов of Buffer, доступных в JDK, используют внутренний массив для хранения элементов.
Class
Has Array?
ByteBuffer
true
MappedByteBuffer
true
ShortBuffer
true
IntBuffer
true
FloatBuffer
true
LongBuffer
true
DoubleBuffer
true
CharBuffer
true
Например:
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();
Возвращает значение true, если этот Buffer использует массивы в качестве метода хранения элементов, в противном случае возвращает значение false. Это опциональная операция, которая может не поддерживаться в подклассе of Buffer.
Большинство подклассов of Buffer, доступных в JDK, используют внутренний массив для хранения элементов.
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();
Проверяет, доступен ли этот Buffer только для чтения (read-only) или нет. Это опциональная операция (optional operation), которая может не поддерживаться в подклассе Buffer, и будет выдано исключение UnsupportedOperationException.
Большинство подклассов of Buffer, доступных в JDK, поддерживают как режимы чтения, так и записи (по умолчанию):
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
Например:
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
Например: Создайте 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)
Например: Создайте другиеread-only Buffer: 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();
No ADS