Обоснование дизайна публичного интерфейса ByteArrayOutputStream? - PullRequest
4 голосов
/ 13 июня 2011

Существует много стандартных библиотек java и сторонних библиотек, в публичном API которых есть методы для записи или чтения из Stream.Один пример - javax.imageio.ImageIO.write(), для записи содержимого обработанного изображения в него требуется OutputStream.Другим примером является iText библиотека для обработки PDF, которая берет OutputStream, чтобы записать полученный PDF в нее.Третий пример - API AmazonS3 Java, который берет InputStream, так что он будет считывать его и создавать файл в три хранилище S3 .

Проблема возникает, когда вы хотите объединить два из них.Например, у меня есть изображение BufferedImage, для которого я должен использовать ImageIO.write, чтобы выдать результат в OutputStream.Но не существует прямого способа отправить его в Amazon S3, поскольку S3 требует InputStream.
Существует несколько способов решить эту проблему, но предметом этого вопроса является использование ByteArrayOutputStream.

Идея ByteArrayOutputStream заключается в использовании промежуточного байтового массива, заключенного в Input/Output Stream, чтобы парень, который хочет записать в выходной поток, записал в массив, а парень, который хочет прочитать, прочиталмассив.

Меня интересует, почему ByteArrayOutputStream не разрешает доступ к байтовому массиву без его копирования, например, для предоставления InputStream, который имеет прямой доступ к нему.Единственный способ получить к нему доступ - вызвать toByteArray(), который создаст копию внутреннего массива (стандартного).Это означает, что в моем примере изображения у меня будет три копии изображения в памяти:

  • Первый - это фактический BufferedImage,
  • секунда - это внутренняя array OutputStream, а
  • третья - копия, созданная toByteArray(), поэтому я могу создатьInputStream.

Чем оправдан этот дизайн?

  • Скрытие реализации?Просто укажите getInputStream(), и реализация останется скрытой.
  • Многопоточность?ByteArrayOutputStream не подходит для доступа к нескольким потокам, так что это не может быть.

Более того, существует второй вариант ByteArrayOutputStream, предоставляемый библиотекой Apache commons-io (которая имеет другую внутреннюю реализацию).Но оба имеют одинаковый публичный интерфейс, который не обеспечивает доступ к байтовому массиву без его копирования.

Ответы [ 3 ]

6 голосов
/ 13 июня 2011

Мне интересно, почему ByteArrayOutputStream не разрешает какой-либо доступ к байтовому массиву, не копируя его, например, чтобы обеспечить InputStream, который имеет прямой доступ к нему.

Я могу думать о четырех причинах:

  • Текущая реализация использует один байтовый массив, но он также может быть реализован как связанный список байтовых массивов.отложив создание окончательного массива до тех пор, пока приложение не запросит его.Если бы приложение могло видеть фактический байтовый буфер, оно будет иметь в виде одного массива.

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

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

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


Насколько оправдан этот дизайн?

Дизайн разработан для более простых случаев использования, чем ваш.Библиотеки классов Java SE не стремятся поддерживать все возможные варианты использования.Но они не мешают вам (или сторонней библиотеке) предоставлять другие потоковые классы для других вариантов использования.


Суть в том, что разработчики Sun решили НЕ выставлять массив байтов дляByteArrayOutputStream, и (ИМО) вы вряд ли передумали.

(И если вы хотите попробовать, это не то место, где можно это сделать.

  • Попробуйте отправить RFE через базу данных ошибок.
  • Или разработатьпатч, который добавляет функциональность и передает его команде OpenJDK по соответствующим каналам. Вы увеличили бы свои шансы, если бы включили всесторонние модульные тесты и документацию.)

Возможно, вам удастся убедить ApacheПризывает разработчиков ввода-вывода к правильности ваших аргументов, при условии, что вы можете придумать дизайн API, который не слишком опасен.

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

2 голосов
/ 13 июня 2011

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

2 голосов
/ 13 июня 2011

Я думаю, что вы ищете поведение Труба . ByteArrayOutputStream - это просто OutputStream, а не поток ввода-вывода. Он не был разработан для того, что ты имеешь в виду.

...