Байт-массив неизвестной длины в Java: часть II

Подобно "Байт-массив неизвестной длины в java" Мне нужно уметь записывать неизвестное количество байтов из источника данных в массив byte [], Однако мне нужна возможность читать из ранее сохраненных байтов, для алгоритма сжатия, поэтому ByteArrayOutputStream не работает для меня.

Прямо сейчас у меня есть схема, где я выделяю ByteBuffers фиксированного размера N, добавляя новый, когда я добираюсь до N, 2N, 3N байтов и т.д. После того, как данные исчерпаны, я удаляю все буферы в массив уже известного размера.

Есть ли лучший способ сделать это? Наличие буферов фиксированного размера снижает гибкость алгоритма сжатия.

Ответ 1

Почему вы не подклассом ByteArrayOutputStream? Таким образом, ваш подкласс имеет доступ к защищенным полям buf и count, и вы можете добавлять методы в свой класс для непосредственного управления ими.

Ответ 2

Как насчет использования циклического байтового буфера? Он имеет возможность динамически расти и эффективен.

Здесь реализована реализация: http://ostermiller.org/utils/CircularByteBuffer.java.html

Ответ 3

Расход ByteArrayOutputStream - это изменение размера базового массива. Ваша фиксированная блокировка блоков устраняет большую часть этого. Если изменение размера недостаточно дорого для вас, то есть при тестировании ByteArrayOutputStream "достаточно быстро" и не обеспечивает отрицательного давления памяти), то, возможно, подклассы ByteArrayOutputStream, как было предложено vanza, будут работать на вас.

Я не знаю вашего алгоритма сжатия, поэтому не могу сказать, почему ваш список блоков делает его менее гибким, или даже почему алгоритм сжатия даже знал о блоках. Но поскольку блоки могут быть динамическими, вы можете настроить размер блока в зависимости от ситуации, чтобы лучше поддерживать разнообразие алгоритма сжатия, который вы используете.

Если алгоритм сжатия может работать с "потоком" (т.е. фиксированными размерами данных), размер блока должен иметь значение, поскольку вы могли бы скрыть все эти детали из реализации. Идеальный мир - если алгоритм сжатия хочет, чтобы его данные в кусках соответствовали размеру блоков, которые вы выделяете, таким образом вам не нужно будет копировать данные для подачи компрессора.

Ответ 4

Хотя вы, безусловно, можете использовать ArrayList для этого, вы в значительной степени смотрите на издержки памяти 4-8 раз - при условии, что байты не были недавно выделены, но поделились одним глобальным экземпляром (поскольку это верно для целых чисел, я предполагаю, что он работает для байтов) - и вы потеряете всю локальность кэша.

Итак, пока вы можете подклассифицировать ByteArrayOutputStream, но даже там вы получаете накладные расходы (методы синхронизированы), которые вам не нужны. Поэтому я лично просто развожу свой собственный класс, который динамически растет, когда вы пишете ему. Менее эффективен, чем ваш текущий метод, но простой, и все мы знаем, что связано с амортизированными издержками - в противном случае вы, очевидно, можете использовать свое решение. Пока вы завершаете решение в чистом интерфейсе, вы скроете сложность и получите хорошую производительность.

Или иначе сказано: Нет, вы в значительной степени не можете сделать это более эффективно, чем то, что вы уже делаете, и каждая встроенная коллекция java должна работать хуже по той или иной причине.

Ответ 5

Как Крис ответил CircularByteBuffer api - это путь. К счастью, сейчас он находится в центральной реке. Выделение фрагмента из этой ссылки, это так просто:

Одиночный пример кругового буфера

// buffer all data in a circular buffer of infinite size
CircularByteBuffer cbb = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
class1.putDataOnOutputStream(cbb.getOutputStream());
class2.processDataFromInputStream(cbb.getInputStream());

Преимущества:

  • Один класс CircularBuffer, а не два класса.
  • Легче конвертировать между подходами "буфер все данные" и "дополнительные потоки".
  • Вы можете изменить размер буфера, а не полагаться на жестко закодированный 1k буфера в трубах.

Наконец, мы свободны от проблем с памятью и труб API

Ответ 6

Для простоты вы можете использовать java.util.ArrayList:

ArrayList<Byte> a = new ArrayList<Byte>();
a.add(value1);
a.add(value2);
...
byte value = a.get(0);

Java 1.5 и выше обеспечит автоматический бокс и распаковку между типами byte и byte. Производительность может быть немного хуже, чем ByteArrayOutputStream, но ее легко читать и понимать.