2009-09-08 9 views
12

Est-ce:Comment initialiser un ByteBuffer si vous ne savez pas combien d'octets allouer au préalable?

ByteBuffer buf = ByteBuffer.allocate(1000); 

... la seule façon d'initialiser un ByteBuffer? Et si je n'ai aucune idée du nombre d'octets que je dois allouer ..?

Modifier: Plus de détails:

Je suis la conversion d'un format de fichier d'image dans un fichier TIFF. Le problème est que le format de fichier de départ peut être de n'importe quelle taille, mais j'ai besoin d'écrire les données dans le TIFF au petit boutiste. Donc je suis en train de lire les choses que je vais imprimer dans le fichier TIFF dans le ByteBuffer d'abord pour pouvoir tout mettre dans Little Endian, ensuite je vais l'écrire dans le fichier outfile. Je suppose que depuis que je sais combien de temps les IFD sont, les en-têtes sont, et je peux probablement comprendre combien d'octets dans chaque plan d'image, je peux simplement utiliser plusieurs ByteBuffers pendant tout ce processus.

Répondre

7

Dépend.

Bibliothèque

Conversion de formats de fichiers tend à être un problème résolu pour la plupart des domaines problématiques. Par exemple:

  • Batik peut transcoder entre différents formats d'image (y compris TIFF).
  • Apache POI peut convertir entre les formats de tableur de bureau.
  • Flexmark peut générer du HTML à partir de Markdown.

La liste est longue. La première question devrait être, "Qu'est-ce que library peut accomplir cette tâche?" Si la performance est une considération, votre temps est probablement mieux dépensé pour optimiser un paquet existant pour répondre à vos besoins que d'écrire un autre outil. (En prime, d'autres gens à profiter du travail centralisé.)


Quantités connues

  • Lecture d'un fichier? Allouer file.size() octets.
  • Copie d'une chaîne? Allouer string.length() octets.
  • Copie d'un paquet TCP? Allouer 1500 octets, par exemple.

inconnues

Lorsque le nombre d'octets est vraiment inconnu, vous pouvez faire quelques petites choses:

  • faire une proposition.
  • Analyser des exemples de jeux de données à mettre en tampon; utilise la longueur moyenne.

Exemple

Java de StringBuffer, sauf instruction contraire, utilise une taille initiale du tampon pour contenir 16 caractères. Une fois les 16 caractères remplis, un nouveau tableau plus long est alloué, puis les 16 caractères originaux copiés. Si le StringBuffer avait une taille initiale de 1024 caractères, la réallocation ne se produirait pas aussi tôt ni aussi souvent.

Optimisation

De toute façon, ce qui est probablement une optimisation prématurée. En règle générale, vous devez allouer un nombre d'octets défini lorsque vous souhaitez réduire le nombre de réaffectations de mémoire interne exécutées.

Il est peu probable que ce soit le goulot d'étranglement de l'application.

+3

Un' StringBuffer' se comporte différemment. Un 'ByteBuffer' ne ferait que lancer une' BufferOverflowException' si vous essayez d'en ajouter d'autres. – bvdb

4

L'idée est que c'est seulement un tampon - pas l'ensemble des données. C'est un lieu de repos temporaire pour les données lorsque vous lisez un morceau, le traitez (éventuellement en l'écrivant ailleurs). Donc, allouez-vous un "morceau" assez grand et normalement ce ne sera pas un problème.

Quel problème anticipez-vous?

10

Les types d'emplacements que vous utiliseriez ByteBuffer correspondent généralement aux types d'emplacements que vous utiliseriez autrement dans un tableau d'octets (qui a également une taille fixe). Avec les E/S synchrones, vous utilisez souvent des tableaux d'octets, avec des E/S asynchrones, les ByteBuffers sont utilisés à la place.

Si vous avez besoin de lire une quantité inconnue de données à l'aide d'un ByteBuffer, envisagez d'utiliser une boucle avec votre tampon et ajouter les données à un ByteArrayOutputStream que vous lisez. Lorsque vous avez terminé, appelez toByteArray() pour obtenir le tableau d'octets final.

À tout moment lorsque vous n'êtes pas absolument sûr de la taille (ou de la taille maximale) d'une entrée donnée, lire en boucle (en utilisant éventuellement un ByteArrayOutputStream, mais sinon traiter les données comme un flux, tel qu'il est lu) est le seul moyen de le gérer. Sans une sorte de boucle, les données restantes seront bien entendu perdues.

Par exemple:

final byte[] buf = new byte[4096]; 
int numRead; 

// Use try-with-resources to auto-close streams. 
try(
    final FileInputStream fis = new FileInputStream(...); 
    final ByteArrayOutputStream baos = new ByteArrayOutputStream() 
) { 
    while ((numRead = fis.read(buf)) > 0) { 
    baos.write(buf, 0, numRead); 
    } 

    final byte[] allBytes = baos.toByteArray(); 

    // Do something with the data. 
} 
catch(final Exception e) { 
    // Do something on failure... 
} 

Si vous vouliez écrire au lieu de Java int s, ou d'autres choses qui ne sont pas octets bruts, vous pouvez envelopper votre ByteArrayOutputStream dans un DataOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
DataOutputStream dos = new DataOutputStream(baos); 

while (thereAreMoreIntsFromSomewhere()) { 
    int someInt = getIntFromSomewhere(); 
    dos.writeInt(someInt); 
} 

byte[] allBytes = baos.toByteArray();  
+0

Pouvez-vous donner un exemple de ceci en utilisant le ByteArrayOutputStream? Comme disons pour un nombre entier? –

+0

@PaulaKristin Check it out maintenant (j'ai aussi corrigé le lien 'ByteArrayOutputStream' cassé) –