В Java мне нужно поместить содержимое из OutputStream (я сам заполняю данные в этот поток) в ByteBuffer. Как это сделать простым способом?
Как поместить данные из OutputStream в ByteBuffer?
Ответы (6)
Вы можете создать ByteArrayOutputStream
и писать в него, а также извлекать содержимое как byte[]
с помощью toByteArray()
. Затем ByteBuffer.wrap(byte [])
создаст ByteBuffer
с содержимым массива выходных байтов.
ByteArrayOutputStream#toByteArray()
создает копию лежащего в основе массива байтов, так что, хотя этот рецепт прост, он не так эффективен, как хотелось бы. Однако, поскольку емкость ByteBuffer
фиксирована, желание записать произвольное количество данных через OutputStream
в ByteBuffer
непримиримо.
- person seh; 02.02.2012
Существует более эффективный вариант ответа @DJClayworth.
Как правильно заметил @seh, ByteArrayOutputStream.toByteArray()
возвращает копию резервного объекта byte[]
, что может быть неэффективным. Однако и вспомогательный объект 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());
В результате, когда все выходные данные записаны, один и тот же буфер используется в качестве основы для входного потока.
PromiscuousByteArrayOutputStream
. Имя намеренно длинное и зловещее.
- person seh; 28.03.2013
Хотя вышеупомянутые ответы решают вашу проблему, ни один из них не является эффективным, как вы ожидаете от NIO. ByteArrayOutputStream или MyByteArrayOutputStream сначала записывают данные в память кучи Java, а затем копируют их в ByteBuffer, что сильно влияет на производительность.
Эффективной реализацией будет самостоятельное написание класса ByteBufferOutputStream. На самом деле это довольно легко сделать. Вы должны просто предоставить метод write(). См. эту ссылку для ByteBufferInputStream.
Попробуйте использовать PipedOutputStream вместо OutputStream. Затем вы можете подключить PipedInputStream для чтения данных обратно из PipedOutputStream.
Вы говорите, что сами пишете в этот поток? Если это так, возможно, вы могли бы реализовать свой собственный 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();
}
/*
}
// доступ к защищенному члену buf и count из класса расширения
class ByteArrayOutputStream2ByteBuffer extends ByteArrayOutputStream {
public ByteBuffer toByteBuffer() {
return ByteBuffer.wrap(buf, 0, count);
}
}