HOWTO · Java

ByteBuffer-Klasse in Java

Erfahren Sie, wie Position, Limit und Kapazität von Java ByteBuffer das Lesen und Schreiben von Bytes steuern, wie Sie einen Puffer erstellen und verwenden und wie zwei Java-Beispiele Bytes und Zeichen mit getChar() lesen.

Auf dieser Seite

Ein ByteBuffer speichert Bytes und verfolgt die nächste Position zum Lesen oder Schreiben. Seine Position, sein Limit und seine Kapazität bestimmen, auf welche Bytes eine Operation zugreifen kann. Diese Anleitung erklärt diese Zustandsänderungen, die wichtigsten Erstellungs- und Zugriffsmethoden sowie zwei eigenständige Java-Beispiele: eines für Byte-Werte und eines für Zeichen.

ByteBuffer-Klasse in Java

java.nio.ByteBuffer ist ein byteorientierter NIO-Puffer, der verwendet wird, um Binärdaten zwischen Java-Code und E/A-Kanälen zu übertragen. Er speichert keine Reihe von Integer-Werten als seine grundlegenden Daten; Methoden wie putInt() kodieren einen primitiven Wert mithilfe der Byte-Reihenfolge des Puffers in Bytes.

Ein Puffer hat drei wichtige Zustandswerte:

  • Kapazität ist die feste Größe des Puffers.
  • Position ist der Index des nächsten Bytes, das ein relatives Lesen oder Schreiben verwenden wird.
  • Limit ist der erste Index, der nicht gelesen oder geschrieben werden kann. Es ist höchstens die Kapazität.

Ein neuer Puffer von allocate() hat die Position 0, ein Limit gleich der Kapazität und keine geschriebenen Daten. Relatives put() rückt die Position vor. Der Aufruf von flip() setzt das Limit auf die aktuelle Position und setzt die Position auf 0 zurück, um die geschriebenen Bytes zum Lesen vorzubereiten. Relatives get() rückt die Position beim Lesen vor. clear() bereitet den gesamten Puffer für ein weiteres Schreiben vor, indem es das Limit auf die Kapazität und die Position auf 0 zurücksetzt; es löscht die Bytes nicht. rewind() setzt die Position auf 0 zurück, während das aktuelle Limit erhalten bleibt, sodass derselbe lesbare Bereich erneut gelesen werden kann.

Die Klassenhierarchie ist Object → Buffer → ByteBuffer. Ein Puffer kann direkt oder nicht-direkt sein. allocateDirect() fordert Speicher außerhalb des gewöhnlichen Java-Heaps an und kann für einige E/A-Muster nützlich sein, ist aber nicht automatisch schneller. Bevorzugen Sie allocate(), es sei denn, direkter Speicher passt zu den gemessenen E/A-Anforderungen der Anwendung.

Operative Kategorien der ByteBuffer-Klasse in Java

ByteBuffer-Operationen fallen in einige praktische Gruppen:

  • Erstellung: allocate(capacity) erstellt einen Heap-Puffer, allocateDirect(capacity) fordert einen direkten Puffer an, und wrap(byteArray) macht ein vorhandenes Byte-Array über einen Puffer zugänglich.
  • Relativer Zugriff: get() und put(byte) verwenden die aktuelle Position und rücken sie vor. Ihre Bulk-Überladungen übertragen mehrere Bytes und rücken um die übertragene Anzahl vor.
  • Absoluter Zugriff: get(index) und put(index, value) adressieren einen bestimmten Index, ohne die Position zu ändern. Der Index muss trotzdem innerhalb des aktuellen Limits liegen.
  • Primitive Werte und Ansichten: getInt(), putInt(value), getChar() und putChar(value) wandeln zwischen primitiven Werten und Bytes um. Die Einstellung order() bestimmt die Byte-Reihenfolge für diese mehrbytigen Werte. Eine Ansicht wie asIntBuffer() interpretiert die Bytes als Integer, ohne die Position des ursprünglichen Puffers zu ändern.
  • Zustand und abgeleitete Puffer: position(), limit(), remaining(), slice(), duplicate(), compact() und rewind() lassen Code den lesbaren und schreibbaren Bereich prüfen oder verwalten.

Die Methodenfamilie wrap() erstellt einen Puffer, der durch ein vorhandenes Array gestützt wird: ByteBuffer.wrap(byte[]) umhüllt Bytes, keine Integer. Um ein int[] zu umhüllen, verwenden Sie IntBuffer.wrap(intArray); es erstellt einen Integer-Puffer und ist eine andere API. Ein Byte-Array kann auch durch einen ByteBuffer als Folge von Bytes betrachtet werden und dann entsprechend seiner Byte-Reihenfolge als primitive Werte interpretiert werden.

Methoden der Byte-Pufferklasse in Java

Die folgenden Methoden decken gängige Aufgaben zu Erstellung, Zustand und Datenzugriff ab:

Method Purpose
allocate(int capacity) Erstellt einen Heap-ByteBuffer mit der angeforderten Kapazität.
allocateDirect(int capacity) Erstellt einen direkten ByteBuffer; die Zuweisung kann andere Kosten haben als Heap-Speicher.
wrap(byte[] array) Erstellt einen Puffer, der durch das bereitgestellte Byte-Array gestützt wird.
capacity() Gibt die feste Kapazität zurück.
position() / position(int) Liest oder setzt den nächsten Index für relativen Zugriff.
limit() / limit(int) Liest oder setzt das Ende des zugänglichen Bereichs.
remaining() Gibt limit() - position() zurück.
get() / put(byte) Liest oder schreibt ein Byte an der aktuellen Position und rückt sie vor.
get(int) / put(int, byte) Liest oder schreibt an einem Index, ohne die Position zu ändern.
getInt() / putInt(int) and getChar() / putChar(char) Liest oder schreibt primitive Werte unter Verwendung der aktuellen Byte-Reihenfolge.
order() / order(ByteOrder) Liest oder setzt die Byte-Reihenfolge, die für mehrbytige primitive Werte verwendet wird.
hasArray() / array() Prüft auf ein zugängliches tragendes Byte-Array und gibt es zurück. Direkte und schreibgeschützte Puffer geben möglicherweise keines frei.
isDirect() Meldet, ob der Puffer direkt ist.
flip() / clear() / rewind() Ändert Position und Limit zum Lesen, Schreiben oder erneuten Lesen.
slice() / duplicate() / compact() Erstellt eine Ansicht, kopiert den Pufferzustand in eine verwandte Ansicht oder verschiebt verbleibende Bytes an den Anfang für weiteres Schreiben.
asIntBuffer() Erstellt eine Integer-Ansicht über die verbleibenden Bytes des Puffers.

Ausführliche API-Details finden Sie in der Java-ByteBuffer-Dokumentation.

Implementierung von Byte Buffer in Java

Dieses eigenständige Beispiel schreibt zwei Byte-Werte, schaltet den Puffer mit flip() in den Lesemodus und liest dann die Werte.

Dieses vollständige Programm weist einen Vier-Byte-Puffer zu, schreibt zwei Werte, dreht ihn zum Lesen um und gibt die Werte und den aktuellen Zustand aus.

import java.nio.ByteBuffer;

public class ByteBufferBytes {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.put((byte) 20);
        buffer.put((byte) 40);
        buffer.flip();

        byte first = buffer.get();
        byte second = buffer.get();
        System.out.println("bytes=" + first + "," + second);
        System.out.println("position=" + buffer.position()
                + ", limit=" + buffer.limit()
                + ", capacity=" + buffer.capacity());
    }
}

Erwartete Ausgabe:

bytes=20,40
position=2, limit=2, capacity=4

Nach den beiden Lesevorgängen ist die Position 2; das Limit bleibt 2, und die Kapazität bleibt 4. Ein absolutes Schreiben wie buffer.put(0, (byte) 7) ändert das Byte am Index 0, ohne die Position vorzurücken. Ein relatives Schreiben verwendet die aktuelle Position und rückt sie vor. Das Setzen von Position oder Limit ist durch den aktuellen Zustand des Puffers eingeschränkt, also setzen Sie das Limit, bevor Sie die Position darüber hinaus bewegen. remaining() meldet, wie viele Bytes zwischen Position und Limit verfügbar sind.

Implementieren der getChar-Methode der Byte-Pufferklasse in Java

getChar() liest zwei Bytes an der aktuellen Position und rückt die Position um zwei vor. Setzen Sie die Byte-Reihenfolge explizit, wenn das Datenformat eines Puffers eine bestimmte Reihenfolge erfordert. Das Beispiel schreibt vier Java-char-Werte mit putChar(), dreht den Puffer um und liest nur, solange mindestens Character.BYTES verbleiben. Dies verwendet die bekannte Byte-Anzahl, um sicher zu stoppen; es hängt nicht von einem Null-Zeichen als Abschluss ab.

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ByteBufferChars {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(4 * Character.BYTES)
                .order(ByteOrder.BIG_ENDIAN);
        buffer.putChar('J');
        buffer.putChar('A');
        buffer.putChar('V');
        buffer.putChar('A');
        buffer.flip();

        StringBuilder text = new StringBuilder();
        while (buffer.remaining() >= Character.BYTES) {
            text.append(buffer.getChar());
        }
        System.out.println(text);

        buffer.rewind();
        char first = buffer.getChar();
        System.out.println("first=" + first);
    }
}

Erwartete Ausgabe:

JAVA
first=J

Ein Java-char ist eine UTF-16-Codeeinheit, und getChar() dekodiert keine beliebige UTF-8-Bytefolge. Lesen Sie für als UTF-8 gespeicherten Text die Bytes und dekodieren Sie sie mit einem Charset wie StandardCharsets.UTF_8. Für dieses Beispiel mit fester Breite beträgt die Puffergröße genau zwei Bytes pro Zeichen, und die Prüfung der verbleibenden Bytes stellt sicher, dass jedes getChar() zwei Bytes verfügbar hat. rewind() macht dann denselben Bereich wieder lesbar, sodass das abschließende Lesen das erste Zeichen abrufen kann.