Как инициализировать ByteBuffer, если вы не знаете, сколько байтов будет выделено заранее?

Это:

ByteBuffer buf = ByteBuffer.allocate(1000);

... единственный способ инициализировать ByteBuffer?

Что делать, если я не знаю, сколько байтов мне нужно выделить..?

Изменить: Подробнее:

Я конвертирую один формат файла изображения в файл TIFF. Проблема заключается в том, что формат исходного файла может быть любого размера, но мне нужно записать данные в TIFF до конца. Поэтому я читаю материал, который в конечном итоге собираюсь напечатать в файле TIFF в ByteBuffer, чтобы я мог поместить все в Little Endian, затем я напишу его в outfile. Я предполагаю, что, поскольку я знаю, сколько длинных IFD, заголовки, и я могу, возможно, выяснить, сколько байтов в каждой плоскости изображения, я могу просто использовать несколько ByteBuffers в течение всего этого процесса.

Ответ 1

Зависит.

Библиотека

Преобразование форматов файлов, как правило, является проблемой для большинства проблемных областей. Например:

  • Batik может транскодировать между различными форматами изображений (включая TIFF).
  • Apache POI может конвертировать между форматами электронных таблиц офиса.
  • Flexmark может генерировать HTML из Markdown.

Список длинный. Первый вопрос должен состоять в том, что "что библиотека может выполнить эту задачу?" Если производительность является соображением, ваше время, вероятно, лучше потратить на оптимизацию существующего пакета для удовлетворения ваших потребностей, чем написание еще одного инструмента. (В качестве бонуса другие люди получают выгоду от централизованной работы.)


Известные количества

  • Чтение файла? Выделите file.size() байты.
  • Копирование строки? Выделите string.length() байты.
  • Копирование TCP-пакета? Выделите 1500 байтов, например.

Неизвестные величины

Когда количество байтов действительно неизвестно, вы можете сделать несколько вещей:

  • Сделайте предположение.
  • Анализ наборов данных примера для буфера; используйте среднюю длину.

Пример

Java StringBuffer, если не указано иное, использует начальный размер буфера для хранения 16 символов. После заполнения 16 символов выделяется новый, более длинный массив, а затем копируются 16 оригиналов. Если StringBuffer имел начальный размер 1024 символа, то перераспределение произойдет не раньше, чем раньше.

Оптимизация

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

Вряд ли это будет узким местом приложения.

Ответ 2

Типы мест, которые вы использовали бы ByteBuffer, обычно являются типами мест, которые в противном случае вы использовали бы массив байтов (который также имеет фиксированный размер). При синхронном вводе-выводе вы часто используете байтовые массивы с асинхронным вводом-выводом, вместо этого используются ByteBuffers.

Если вам нужно прочитать неизвестный объем данных с помощью ByteBuffer, рассмотрите использование цикла с вашим буфером и добавьте данные в ByteArrayOutputStream, когда вы его читаете. Когда вы закончите, вызовите toByteArray(), чтобы получить окончательный массив байтов.

В любое время, когда вы не абсолютно уверены в размере (или максимальном размере) данного ввода, читаете в цикле (возможно, используя ByteArrayOutputStream, но в противном случае просто обрабатываете данные как поток, так как это read) - это единственный способ справиться с этим. Без какой-либо петли любые оставшиеся данные, конечно, будут потеряны.

Например:

final byte[] buf = new byte[4096];
int numRead;

// Use try-with-resources to auto-close streams.
try(
  final FileInputStream fis = new FileInputStream(...);
  final ByteArrayOutputStream baos = new ByteArrayOutputStream()
) {
  while ((numRead = fis.read(buf)) > 0) {
    baos.write(buf, 0, numRead);
  }

  final byte[] allBytes = baos.toByteArray();

  // Do something with the data.
}
catch( final Exception e ) {
  // Do something on failure...
}

Если вы захотели написать Java int s или другие вещи, которые не являются сырыми байтами, вы можете обернуть ваш ByteArrayOutputStream в DataOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);

while (thereAreMoreIntsFromSomewhere()) {
    int someInt = getIntFromSomewhere();
    dos.writeInt(someInt);
}

byte[] allBytes = baos.toByteArray();    

Ответ 3

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

Какую проблему вы ожидаете?