Обоснование дизайна публичного интерфейса ByteArrayOutputStream?
Существует много стандартных библиотек Java и сторонних библиотек, которые в своих общедоступных API-интерфейсах содержат методы для записи или чтения Stream
, Одним из примеров является javax.imageio.ImageIO.write()
это занимает OutputStream
записать на него содержимое обработанного изображения. Другим примером является библиотека обработки iText PDF, которая принимает OutputStream
написать полученный PDF к нему. Третий пример - Amazon API Java API, который принимает 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 ответа
К счастью, внутренний массив protected
, так что вы можете разбить его на подклассы и обернуть ByteArrayInputStream
вокруг, без всякого копирования.
Мне интересно, почему ByteArrayOutputStream не разрешает какой-либо доступ к байтовому массиву, не копируя его, например, чтобы обеспечить InputStream, который имеет прямой доступ к нему.
Я могу думать о четырех причинах:
Текущая реализация использует один байтовый массив, но он также может быть реализован как связанный список байтовых массивов, откладывая создание окончательного массива до тех пор, пока приложение не запросит его. Если бы приложение могло видеть фактический байтовый буфер, это должен был быть один массив.
Вопреки вашему пониманию
ByteArrayOutputStream
Потокобезопасен и подходит для использования в многопоточных приложениях. Но если был предоставлен прямой доступ к байтовому массиву, трудно понять, как это можно синхронизировать, не создавая других проблем.API-интерфейс должен быть более сложным, поскольку приложению также необходимо знать, где находится текущая верхняя отметка буфера и является ли байтовый массив (все еще) динамическим байтовым массивом. (The
ByteArrayOutputStream
реализация иногда требует перераспределения байтового массива... и это приведет к тому, что приложение будет содержать ссылку на массив, который больше не является массивом.)Когда вы предоставляете байтовый массив, вы позволяете приложению изменять содержимое массива, что может быть проблематично.
Насколько этот дизайн оправдан?
Дизайн разработан для более простых вариантов использования, чем ваш. Библиотеки классов Java SE не стремятся поддерживать все возможные варианты использования. Но они не мешают вам (или сторонней библиотеке) предоставлять другие потоковые классы для других сценариев использования.
Суть в том, что дизайнеры Sun решили НЕ выставлять массив байтов для ByteArrayOutputStream
и (ИМО) вы вряд ли передумали.
(И если вы хотите попробовать, это не то место, чтобы сделать это.
- Попробуйте отправить запрос в базу данных ошибок.
- Или разработайте патч, который добавляет функциональность, и отправьте его команде OpenJDK по соответствующим каналам. Вы бы повысили свои шансы, если бы включили комплексные юнит-тесты и документацию.)
Возможно, вам удастся убедить разработчиков ввода-вывода Apache Commons в правильности ваших аргументов при условии, что вы сможете придумать дизайн API, который не слишком опасен.
Кроме того, ничто не мешает вам просто реализовать собственную версию специального назначения, которая предоставляет свои внутренние структуры данных. Код написан под GPL, поэтому вы можете скопировать его... при условии соблюдения обычных правил GPL о распространении кода.
Я думаю, что поведение, которое вы ищете, это Труба. ByteArrayOutputStream - это просто OutputStream, а не поток ввода-вывода. Он не был разработан для того, что вы имеете в виду.