Java 如何将输出流中的数据放入ByteBuffer?

Java 如何将输出流中的数据放入ByteBuffer?,java,outputstream,bytebuffer,Java,Outputstream,Bytebuffer,在Java中,我需要将输出流中的内容(我自己将数据填充到该流)放入ByteBuffer。如何以一种简单的方式执行此操作?尝试使用PipedoutpOutstream而不是OutputStream。然后,您可以连接PipedInputStream以从PipedOutStream中读取数据。您可以通过teArrayoutPutStream创建一个并写入,然后使用toByteArray()将内容提取为字节[]。然后ByteBuffer.wrap(byte[])将使用输出字节数组的内容创建一个ByteB

在Java中,我需要将输出流中的内容(我自己将数据填充到该流)放入ByteBuffer。如何以一种简单的方式执行此操作?

尝试使用PipedoutpOutstream而不是OutputStream。然后,您可以连接PipedInputStream以从PipedOutStream中读取数据。

您可以通过teArrayoutPutStream创建一个
并写入,然后使用
toByteArray()
将内容提取为
字节[]
。然后
ByteBuffer.wrap(byte[])
将使用输出字节数组的内容创建一个
ByteBuffer

有一个更有效的@DJClayworth答案变体

正如@seh正确注意到的,
ByteArrayOutputStream.toByteArray()
返回备份
byte[]
对象的副本,这可能效率低下。但是,backing
byte[]
对象以及字节计数都是受
ByteArrayOutputStream
类保护的成员。因此,您可以创建自己的ByteArrayOutputStream变体,直接公开它们:

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

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

  public int getCount() {
    return count;
  }

  public byte[] getBuf() {
    return buf;
  }
}
使用该类很容易:

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

因此,一旦所有输出都被写入,相同的缓冲区就被用作输入流的基础。

尽管上面提到的答案解决了您的问题,但没有一个像您从NIO期望的那样高效。ByteArrayOutputStream或MyByteArrayOutputStream首先将数据写入Java堆内存,然后将其复制到ByteBuffer,这将极大地影响性能


一个有效的实现是自己编写ByteBufferOutputStream类。实际上这很容易做到。您只需提供一个write()方法。有关详细信息,请参阅此链接

你说你自己在写这条流?如果是这样,也许您可以实现自己的ByteBufferOutputStream并即插即用

基类如下所示:

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();
    }
    /*
}
公共类ByteBufferOutputStream扩展了OutputStream{
//Techannel wbc;保护可写性//如果需要直接写入通道
受保护的静态int-bs=2*1024*1024;//2MB缓冲区大小,根据需要更改
受缓冲区bb保护=缓冲区分配(bs);
公共ByteBufferOutputStream(…){
//wbc=…//再次用于写入通道
}
@凌驾
公共无效写入(int i)引发IOException{
如果(!bb.haslaining())flush();
字节b=(字节)i;
bb.put(b);
}
@凌驾
公共无效写入(字节[]b,int off,int len)引发IOException{
如果(bb.剩余()
//从扩展类访问受保护的成员buf计数

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

通常,您不能直接从输出流读取数据。如果您自己填充数据,为什么不能也将其填充到ByteBuffer中呢?如果您创建继承form OutputStream的类并使其能够直接读取,则可以间接读取数据,也可以直接读取数据。我自己并没有填充数据,一些框架填充了数据(当然框架的代码我也不想碰)。请看我的其他评论和答案。这正是我在阅读你的评论之前所做的。所以我可以同意这一点:))不幸的是,
ByteArrayOutputStream#toByteArray()
制作了底层字节数组的副本,所以尽管这个处方很简单,但它并没有我们想要的那么有效。然而,由于
ByteBuffer
的容量是固定的,因此通过
OutputStream
将任意数量的数据写入
ByteBuffer
的愿望是不可调和的。@seh不一定要这样-见我的答案。看起来不错。通过调用ByteArrayOutputStream的方法newInputStream,可以对其进行类似的思考。然而,还有更具前瞻性的解决方案——将由TearrayOutstream备份的数组直接包装到ByteBuffer中。我就是这么做的。说得好,马克。在我的几个Java项目中都有这样一个类,通常被限制为名为
PromiscuousByteArrayOutputStream
的包私有类。这个名称故意很长而且不祥。不需要getCount(),因为ByteArrayOutputStream.size()返回count。因为我们可能需要为OutputStream重用现有的ByteBuffer(及其底层数组),而不是分配一个新数组来获取新的ByteBuffer,我相信这个答案更容易接受。