Перейти к основному содержимому

Java InputStream для байтового массива и байтового буфера

· 5 мин. чтения

Задача: Наибольшая подстрока без повторений

Для заданной строки s, найдите длину наибольшей подстроки без повторяющихся символов. Подстрока — это непрерывная непустая последовательность символов внутри строки...

ANDROMEDA 42

1. Обзор

В этом кратком руководстве мы рассмотрим, как преобразовать InputStream в byte[] и ByteBuffer — сначала с помощью обычной Java, затем с помощью Guava и Commons IO.

Эта статья является частью серии «Java — Back to Basic» здесь, на ForEach.

2. Преобразование в байтовый массив

Давайте рассмотрим получение массива байтов из простых входных потоков . Важным аспектом байтового массива является то, что он обеспечивает индексированный (быстрый) доступ к каждому 8-битному (байтовому) значению, хранящемуся в памяти . Следовательно, вы можете манипулировать этими байтами, чтобы контролировать каждый бит. Мы рассмотрим, как преобразовать простой входной поток в byte[] — сначала с помощью обычной Java, затем с помощью Guava и Apache Commons IO .

2.1. Преобразование с использованием обычной Java

Начнем с решения Java, ориентированного на работу с потоком фиксированного размера:

@Test
public void givenUsingPlainJavaOnFixedSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect()
throws IOException {
InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
byte[] targetArray = new byte[is.available()];

is.read(targetArray);
}

В случае буферизованного потока, когда мы имеем дело с буферизованным потоком и не знаем точного размера базовых данных, нам нужно сделать реализацию более гибкой:

@Test
public void givenUsingPlainJavaOnUnknownSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect()
throws IOException {
InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5, 6 }); // not really known
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int nRead;
byte[] data = new byte[4];

while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}

buffer.flush();
byte[] targetArray = buffer.toByteArray();
}

Начиная с Java 9, мы можем добиться того же с помощью специального метода readNbytes :

@Test
public void givenUsingPlainJava9OnUnknownSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect()
throws IOException {
InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2, 3, 4, 5, 6 });
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int nRead;
byte[] data = new byte[4];

while ((nRead = is.readNBytes(data, 0, data.length)) != 0) {
System.out.println("here " + nRead);
buffer.write(data, 0, nRead);
}

buffer.flush();
byte[] targetArray = buffer.toByteArray();
}

Разница между этими двумя методами очень тонкая.

Первый read(byte[] b, int off, int len) считывает до len байтов данных из входного потока , а второй readNBytes(byte[] b, int off, int len) , считывает точно запрошенное количество байтов .

Кроме того, read возвращает -1 , если во входном потоке больше нет доступных данных. Однако readNbytes всегда возвращает фактическое количество байтов, прочитанных в буфер.

Мы также можем прочитать все байты сразу:

@Test
public void
givenUsingPlainJava9_whenConvertingAnInputStreamToAByteArray_thenCorrect()
throws IOException {
InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2 });

byte[] data = is.readAllBytes();
}

2.2. Конвертировать с помощью гуавы

Давайте теперь посмотрим на простое решение на основе Guava — используя удобный служебный класс ByteStreams:

@Test
public void givenUsingGuava_whenConvertingAnInputStreamToAByteArray_thenCorrect()
throws IOException {
InputStream initialStream = ByteSource.wrap(new byte[] { 0, 1, 2 }).openStream();

byte[] targetArray = ByteStreams.toByteArray(initialStream);
}

2.3. Преобразование с использованием Commons IO

И, наконец, простое решение с использованием Apache Commons IO:

@Test
public void givenUsingCommonsIO_whenConvertingAnInputStreamToAByteArray_thenCorrect()
throws IOException {
ByteArrayInputStream initialStream = new ByteArrayInputStream(new byte[] { 0, 1, 2 });

byte[] targetArray = IOUtils.toByteArray(initialStream);
}

Метод IOUtils.toByteArray() буферизует входные данные внутри, поэтому нет необходимости использовать экземпляр BufferedInputStream , когда требуется буферизация.

3. Преобразовать в байтовый буфер

Теперь давайте посмотрим на получение ByteBuffer из InputStream. Это полезно всякий раз, когда нам нужно выполнять быстрые и прямые низкоуровневые операции ввода-вывода в памяти .

Используя тот же подход, что и в предыдущих разделах, мы рассмотрим, как преобразовать InputStream в ByteBuffer — сначала с помощью обычной Java, затем с помощью Guava и Commons IO.

3.1. Преобразование с использованием обычной Java

В случае с байтовым потоком мы знаем точный размер базовых данных. Давайте используем метод ByteArrayInputStream#available для чтения потока байтов в ByteBuffer :

@Test
public void givenUsingCoreClasses_whenByteArrayInputStreamToAByteBuffer_thenLengthMustMatch()
throws IOException {
byte[] input = new byte[] { 0, 1, 2 };
InputStream initialStream = new ByteArrayInputStream(input);
ByteBuffer byteBuffer = ByteBuffer.allocate(3);
while (initialStream.available() > 0) {
byteBuffer.put((byte) initialStream.read());
}

assertEquals(byteBuffer.position(), input.length);
}

3.2. Конвертировать с помощью гуавы

Давайте теперь рассмотрим простое решение на основе Guava — с помощью удобного служебного класса ByteStreams :

@Test
public void givenUsingGuava__whenByteArrayInputStreamToAByteBuffer_thenLengthMustMatch()
throws IOException {
InputStream initialStream = ByteSource
.wrap(new byte[] { 0, 1, 2 })
.openStream();
byte[] targetArray = ByteStreams.toByteArray(initialStream);
ByteBuffer bufferByte = ByteBuffer.wrap(targetArray);
while (bufferByte.hasRemaining()) {
bufferByte.get();
}

assertEquals(bufferByte.position(), targetArray.length);
}

Здесь мы используем цикл while с методом hasRemaining , чтобы показать другой способ чтения всех байтов в ByteBuffer. В противном случае утверждение завершится ошибкой, поскольку позиция индекса ByteBuffer будет равна нулю.

3.3. Преобразование с использованием Commons IO

И наконец — использование Apache Commons IO и класса IOUtils :

@Test
public void givenUsingCommonsIo_whenByteArrayInputStreamToAByteBuffer_thenLengthMustMatch()
throws IOException {
byte[] input = new byte[] { 0, 1, 2 };
InputStream initialStream = new ByteArrayInputStream(input);
ByteBuffer byteBuffer = ByteBuffer.allocate(3);
ReadableByteChannel channel = newChannel(initialStream);
IOUtils.readFully(channel, byteBuffer);

assertEquals(byteBuffer.position(), input.length);
}

4. Вывод

В этой статье показаны различные способы преобразования необработанного входного потока в массив байтов и байтовый буфер с использованием простой Java, Guava и Apache Commons IO.

Реализацию всех этих примеров можно найти в нашем проекте на GitHub .