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:
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.
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.
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é.
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.
Il peut également être utile de jeter un œil à Netty's DynamicChannelBuffer . Les choses que je trouve utiles sont:
slice(int index, int length)
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.
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);
}
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.