Java Language
ByteBuffer
Ricerca…
introduzione
La classe ByteBuffer
è stata introdotta in java 1.4 per facilitare il lavoro sui dati binari. È particolarmente adatto per l'uso con dati di tipo primitivi. Permette la creazione, ma anche la successiva manipolazione di un byte[]
s su un livello di astrazione più alto
Sintassi
- byte [] arr = new byte [1000];
- ByteBuffer buffer = ByteBuffer.wrap (arr);
- ByteBuffer buffer = ByteBuffer.allocate (1024);
- ByteBuffer buffer = ByteBuffer.allocateDirect (1024);
- byte b = buffer.get ();
- byte b = buffer.get (10);
- short s = buffer.getShort (10);
- buffer.put ((byte) 120);
- buffer.putChar ( 'a');
Utilizzo di base - Creazione di un ByteBuffer
Esistono due modi per creare un ByteBuffer
, in cui è possibile suddividerlo nuovamente.
Se hai un byte[]
già esistente byte[]
, puoi "racchiuderlo" in un ByteBuffer
per semplificare l'elaborazione:
byte[] reqBuffer = new byte[BUFFER_SIZE];
int readBytes = socketInputStream.read(reqBuffer);
final ByteBuffer reqBufferWrapper = ByteBuffer.wrap(reqBuffer);
Questa sarebbe una possibilità per il codice che gestisce le interazioni di rete di basso livello
Se non si dispone di un byte[]
già esistente byte[]
, è possibile creare un ByteBuffer
su un array specificamente assegnato per il buffer in questo modo:
final ByteBuffer respBuffer = ByteBuffer.allocate(RESPONSE_BUFFER_SIZE);
putResponseData(respBuffer);
socketOutputStream.write(respBuffer.array());
Se il percorso del codice è estremamente critico per le prestazioni ed è necessario l'accesso diretto alla memoria di sistema , ByteBuffer
può anche allocare buffer diretti usando #allocateDirect()
Utilizzo di base: scrittura dei dati nel buffer
Data un'istanza ByteBuffer
possibile scrivere dati di tipo primitivo usando la put
relativa e assoluta . La sorprendente differenza è che mettendo i dati usando il metodo relativo si tiene traccia dell'indice in cui i dati sono inseriti, mentre il metodo assoluto richiede sempre un indice per put
i dati.
Entrambi i metodi consentono chiamate "concatenate" . Dato un buffer sufficientemente grande, si può fare quanto segue:
buffer.putInt(0xCAFEBABE).putChar('c').putFloat(0.25).putLong(0xDEADBEEFCAFEBABE);
che è equivalente a:
buffer.putInt(0xCAFEBABE);
buffer.putChar('c');
buffer.putFloat(0.25);
buffer.putLong(0xDEADBEEFCAFEBABE);
Si noti che il metodo operativo su byte
s non è denominato appositamente. Nota inoltre che è anche valido per passare sia un ByteBuffer
sia un byte[]
da put
. Oltre a questo, tutti i tipi primitivi hanno metodi di put
specializzati.
Una nota aggiuntiva: l'indice dato quando si usa la put*
assoluta put*
viene sempre conteggiato in byte
s.
Utilizzo di base: utilizzo di DirectByteBuffer
DirectByteBuffer
è un'implementazione speciale di ByteBuffer
che non ha byte[]
posto sotto.
Possiamo assegnare tale ByteBuffer chiamando:
ByteBuffer directBuffer = ByteBuffer.allocateDirect(16);
Questa operazione alloca 16 byte di memoria. Il contenuto dei buffer diretti può risiedere al di fuori del normale heap spazzato.
Possiamo verificare se ByteBuffer è diretto chiamando:
directBuffer.isDirect(); // true
Le caratteristiche principali di DirectByteBuffer
è che JVM proverà a lavorare in modo nativo sulla memoria allocata senza alcun buffer aggiuntivo, in modo che le operazioni eseguite su di esso possano essere più veloci di quelle eseguite su ByteBuffer con array sottostanti.
Si consiglia di utilizzare DirectByteBuffer
con pesanti operazioni IO che si basano sulla velocità di esecuzione, come la comunicazione in tempo reale.
Dobbiamo essere consapevoli che se proviamo ad usare il metodo array()
otterremo UnsupportedOperationException
. Quindi è una buona pratica per controllare se il nostro ByteBuffer lo ha (array di byte) prima di provare ad accedervi:
byte[] arrayOfBytes;
if(buffer.hasArray()) {
arrayOfBytes = buffer.array();
}
Un altro uso del buffer di byte diretto è l'interoperabilità con JNI. Poiché un buffer di byte diretto non usa un byte[]
, ma un effettivo blocco di memoria, è possibile accedere a quella memoria direttamente tramite un puntatore nel codice nativo. Ciò può far risparmiare un po 'di problemi e sovraccarico sul marshalling tra la rappresentazione Java e nativa dei dati.
L'interfaccia JNI definisce diverse funzioni per gestire i buffer di byte diretti: supporto NIO .