Growing ByteBuffer

Viewed 49376

Has anyone has ever seen an implementation of java.nio.ByteBuffer that will grow dynamically if a putX() call overruns the capacity?

The reason I want to do it this way is twofold:

  1. I don't know how much space I need ahead of time.
  2. I'd rather not do a new ByteBuffer.allocate() then a bulk put() every time I run out of space.
11 Answers

It may be also worth to have a look at Netty's DynamicChannelBuffer. Things that I find handy are:

  • slice(int index, int length)
  • unsigned operations
  • separated writer and reader indexes

Another option is to use direct memory with a large buffer. This consumes virtual memory but only uses as much physical memory as you use (by page which is typically 4K)

So if you allocate a buffer of 1 MB, it comsumes 1 MB of virtual memory, but the only OS gives physical pages to the application which is actually uses.

The effect is you see your application using alot of virtual memory but a relatively small amount of resident memory.

Indeed, auto-extending buffers are so much more intuitive to work with. If you can afford the performance luxury of reallocation, why wouldn't you!?

Netty's ByteBuf gives you exactly this. It's like they've taken java.nio's ByteBuffer and scraped away the edges, making it much easier to use.

Furthermore, it's on Maven in an independent netty-buffer package so you don't need to include the full Netty suite to use.

I'd suggest using an input stream to receive data from a file (with a sperate thread if you need non-blocking) then read bytes into a ByteArrayOutstream which gives you the ability to get it as a byte array. Heres a simple example without adding too many workarounds.

    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); 
    }

Another solution for this would be to allocate more than enough memory, fill the ByteBuffer and then only return the occupied byte array:

Initialize a big ByteBuffer:

ByteBuffer byteBuffer = ByteBuffer.allocate(1000);

After you're done putting things into it:

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

However, using a org.apache.commons.io.output.ByteArrayOutputStream from the start would probably be the best solution.

Netty ByteBuf is pretty good on that.

Related