web-dev-qa-db-fra.com

ByteBuffer croissant

Quelqu'un a-t-il déjà vu une implémentation de Java.nio.ByteBuffer qui se développera dynamiquement si un appel putX () dépasse la capacité?

La raison pour laquelle je veux procéder de cette façon est double:

  1. Je ne sais pas combien d'espace j'ai besoin à l'avance.
  2. Je préfère ne pas faire un nouveau ByteBuffer.allocate () puis un gros put () chaque fois que je manque d'espace.
42
Seth

Pour que les E/S asynchrones fonctionnent, vous devez disposer d'une mémoire continue. En C, vous pouvez tenter de réallouer un tableau, mais en Java vous devez allouer de la nouvelle mémoire. Vous pouvez écrire dans un ByteArrayOutputStream, puis le convertir en ByteBuffer au moment où vous êtes prêt à l'envoyer. L'inconvénient est que vous copiez de la mémoire, et l'une des clés de l'efficacité IO réduit le nombre de fois que la mémoire est copiée.

37
brianegge

Un ByteBuffer ne peut pas vraiment fonctionner de cette façon, car son concept de conception doit être juste voir d'un tableau spécifique, auquel vous pouvez également avoir une référence directe. Il ne pouvait pas essayer d'échanger ce tableau pour un tableau plus grand sans bizarrerie.

Ce que vous souhaitez utiliser est un DataOutput. Le moyen le plus pratique est d'utiliser la bibliothèque (pré-version) de Guava:

ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.write(someBytes);
out.writeInt(someInt);
// ...
return out.toByteArray();

Mais vous pouvez également créer un DataOutputStream à partir d'un ByteArrayOutputStream manuellement, et simplement traiter les fausses IOExceptions en les enchaînant dans AssertionErrors.

10
Kevin Bourrillion

Jetez un œil à Mina IOBuffer https://mina.Apache.org/mina-project/userguide/ch8-iobuffer/ch8-iobuffer.html qui est une goutte de remplacement (il enveloppe le ByteBuffer)

Cependant, je vous suggère d'allouer plus que ce dont vous avez besoin et ne vous en faites pas trop. Si vous allouez un tampon (en particulier un tampon direct), le système d'exploitation lui donne de la mémoire virtuelle, mais il n'utilise la mémoire physique que lorsqu'elle est réellement utilisée. La mémoire virtuelle devrait être très bon marché.

6
Peter Lawrey

Une autre option consiste à utiliser la mémoire directe avec un grand tampon. Cela consomme de la mémoire virtuelle mais utilise uniquement autant de mémoire physique que vous utilisez (par page, qui est généralement 4K)

Donc, si vous allouez un tampon de 1 Mo, cela représente 1 Mo de mémoire virtuelle, mais le seul système d'exploitation donne des pages physiques à l'application qui est réellement utilisée.

L'effet est que votre application utilise beaucoup de mémoire virtuelle mais une quantité relativement faible de mémoire résidente.

5
Peter Lawrey

Il peut également être utile de jeter un œil à Netty's DynamicChannelBuffer . Les choses que je trouve utiles sont:

  • slice(int index, int length)
  • opérations non signées
  • index d'écriture et de lecture séparés
4
anonymous

En effet, les tampons à extension automatique sont tellement plus intuitifs à utiliser. Si vous pouvez vous offrir le luxe de la réaffectation, pourquoi ne le feriez-vous pas?

Netty's ByteBuf vous donne exactement cela. C'est comme s'ils avaient pris ByteBuffer de Java.nio Et gratté les bords, ce qui le rend beaucoup plus facile à utiliser.

De plus, c'est sur Maven dans un package netty-buffer indépendant, vous n'avez donc pas besoin d'inclure la suite Netty complète à utiliser.

1
antak

Je suggère d'utiliser un flux d'entrée pour recevoir des données d'un fichier (avec un thread sperate si vous avez besoin de non-blocage), puis de lire les octets dans un ByteArrayOutstream qui vous donne la possibilité de les obtenir sous forme de tableau d'octets. Voici un exemple simple sans ajouter trop de solutions de contournement.

    try (InputStream inputStream = Files.newInputStream(
            Paths.get("filepath"), StandardOpenOption.READ)){

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int byteRead = 0;

        while(byteRead != -1){
            byteRead = inputStream.read();
            baos.write(byteRead);
        }
        ByteBuffer byteBuffer = ByteBuffer.allocate(baos.size())
        byteBuffer.put(baos.toByteArray());

        //. . . . use the buffer however you want

    }catch(InvalidPathException pathException){
        System.out.println("Path exception: " + pathException);
    }
    catch (IOException exception){
        System.out.println("I/O exception: " + exception); 
    }
1
Full Stack

Une autre solution serait d'allouer plus de mémoire que nécessaire, de remplir le ByteBuffer et de ne renvoyer ensuite que le tableau d'octets occupé:

Initialisez un grand ByteBuffer:

ByteBuffer byteBuffer = ByteBuffer.allocate(1000);

Une fois que vous avez fini d'y mettre les choses:

private static byte[] getOccupiedArray(ByteBuffer byteBuffer)
{
    int position = byteBuffer.position();
    return Arrays.copyOfRange(byteBuffer.array(), 0, position);
}

Cependant, en utilisant un org.Apache.commons.io.output.ByteArrayOutputStream dès le départ serait probablement la meilleure solution.

0
BullyWiiPlaza