25

In Java I need to put content from an OutputStream (I fill data to that stream myself) into a ByteBuffer. How to do it in a simple way?

2
  • 1
    Generally, you can't read directly from an output stream. If you fill the data yourself, why can't you fill it into the ByteBuffer as well? Commented Apr 26, 2010 at 20:28
  • It is possible to read from it indirectly and of cause directly as well if you create class that inherits form OutputStream and make it possible to read from it directly. I did not fill the data myself, some framework did(and the framework's code I don't want to touch of cause). See my other comment and answers as well. Commented Apr 30, 2010 at 21:19

7 Answers 7

36

You can create a ByteArrayOutputStream and write to it, and extract the contents as a byte[] using toByteArray(). Then ByteBuffer.wrap(byte []) will create a ByteBuffer with the contents of the output byte array.

Sign up to request clarification or add additional context in comments.

3 Comments

That is exactly what I've done just before I've read your comment. So I can approve that this works :))
Unfortunately, ByteArrayOutputStream#toByteArray() makes a copy of the underlying byte array, so while this prescription is simple, it's not as efficient as we'd like. However, since ByteBuffer is fixed in capacity, the desire for writing an arbitrary amount of data through an OutputStream into a ByteBuffer is irreconcilable.
@seh It does not have to be this way - see my answer.
28

There is a more efficient variant of @DJClayworth's answer.

As @seh correctly noticed, ByteArrayOutputStream.toByteArray() returns a copy of the backing byte[] object, which may be inefficient. However, the backing byte[] object as well as the count of the bytes are both protected members of the ByteArrayOutputStream class. Hence, you can create your own variant of the ByteArrayOutputStream exposing them directly:

public class MyByteArrayOutputStream extends ByteArrayOutputStream {
  public MyByteArrayOutputStream() {
  }

  public MyByteArrayOutputStream(int size) {
    super(size);
  }

  public int getCount() {
    return count;
  }

  public byte[] getBuf() {
    return buf;
  }
}

Using this class is easy:

MyByteArrayOutputStream out = new MyByteArrayOutputStream();
fillTheOutputStream(out);
return new ByteArrayInputStream(out.getBuf(), 0, out.getCount());

As a result, once all the output is written the same buffer is used as the basis of an input stream.

4 Comments

Good point, @mark. I have such a class in several of my Java projects, usually restricted as a package-private class called PromiscuousByteArrayOutputStream. The name is deliberately long and ominous.
getCount() is not needed since ByteArrayOutputStream.size() returns count.
To point out a potential pitfall: Unlike toByteArray(), the result of this custom getBuf method will not be trimmed to the ByteArrayOutputStream's size(). If you wrap it into a ByteBuffer, your ByteBuffer will therefore be larger than expected and contain null bytes at the end.
@JT getCount() necessary because it gives us the valid number of bytes that contain actual data. Size gives us the total number of bytes allocated by the output streams.
6

Though the above-mention answers solve your problem, none of them are efficient as you expect from NIO. ByteArrayOutputStream or MyByteArrayOutputStream first write the data into a Java heap memory and then copy it to ByteBuffer which greatly affects the performance.

An efficient implementation would be writing ByteBufferOutputStream class yourself. Actually It's quite easy to do. You have to just provide a write() method. See this link for ByteBufferInputStream.

1 Comment

since we may need to reuse an existing ByteBuffer (and its underlying array) for the OutputStream instead of allocating a new array to get a new ByteBuffer, I believe this answer is more acceptable.
5

// access the protected member buf & count from the extend class

class ByteArrayOutputStream2ByteBuffer extends ByteArrayOutputStream {
    public ByteBuffer toByteBuffer() {
        return ByteBuffer.wrap(buf, 0, count);
    }
}

Comments

1

Try using PipedOutputStream instead of OutputStream. You can then connect a PipedInputStream to read the data back out of the PipedOutputStream.

1 Comment

Looks fine. Similar think can be done with ByteArrayOutputStream by calling its method newInputStream. However there is more strightforward solution - to wrap array that backs up ByteArrayOutputStream into ByteBuffer directly. That is what I've done.
1

You say you're writing to this stream yourself? If so, maybe you could implement your own ByteBufferOutputStream and plug n' play.

The base class would look like so:

public class ByteBufferOutputStream extends OutputStream {
    //protected WritableByteChannel wbc; //if you need to write directly to a channel
    protected static int bs = 2 * 1024 * 1024; //2MB buffer size, change as needed
    protected ByteBuffer bb = ByteBuffer.allocate(bs);

    public ByteBufferOutputStream(...) {
        //wbc = ... //again for writing to a channel
    }

    @Override
    public void write(int i) throws IOException {
        if (!bb.hasRemaining()) flush();
        byte b = (byte) i;
        bb.put(b);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        if (bb.remaining() < len) flush();
        bb.put(b, off, len);
    }

    /* do something with the buffer when it's full (perhaps write to channel?)
    @Override
    public void flush() throws IOException {
        bb.flip();
        wbc.write(bb);
        bb.clear();
    }

    @Override
    public void close() throws IOException {
        flush();
        wbc.close();
    }
    /*
}

Comments

0

See efficient implementation of ByteBuffer backed OutputStream with dynamic re-allocation.

/**
 * Wraps a {@link ByteBuffer} so it can be used like an {@link OutputStream}. This is similar to a
 * {@link java.io.ByteArrayOutputStream}, just that this uses a {@code ByteBuffer} instead of a
 * {@code byte[]} as internal storage.
 */
public class ByteBufferOutputStream extends OutputStream {

    private ByteBuffer wrappedBuffer;
    private final boolean autoEnlarge;

https://gist.github.com/hoijui/7fe8a6d31b20ae7af945

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.