2010-06-03 2 views
32

Имеет ли смысл всегда переносить InputStream как BufferedInputStream, когда я знаю, является ли данный InputStream чем-то иным, чем буферизированным? Например, например:Должен ли я всегда включать InputStream в качестве BufferedInputStream?

InputStream is = API.getFromSomewhere() 
if(!(is instanceof BufferedInputStream)) 
    return new BufferedInputStream(is); 
return is; 

ответ

5

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

Если потребитель нуждается в нем, его лучше обернуть.

+5

Это, по-видимому, означает, что отметка и сброс являются единственными полезными вещами, которые BufferedInputStream добавляет поверх простого 'InputStream'. Это может быть справедливо с точки зрения API, но, как говорили другие, «BufferedInputStream» заботится о буферизации чтения для вас. Чтение байта по времени из голого «FileInputStream» в 40 раз медленнее, чем чтение из одного, завернутого в «BufferedInputStream». Тем не менее, верните «InputStream» и сохраните свою подпись метода как таковой. Пользователи могут обернуть, если они того пожелают. – jasonmp85

+0

Согласен, что с точки зрения производительности лучше обернуть его в 99,9% случаев. Однако он освобождает потребителя от ответственности за то, как использовать InputStream. Подобные предположения от потребителя ограничивают возможность повторного использования. –

+0

Я думаю, что в более чем 0,1% случаев потребитель не будет читать один байт за раз и вместо этого сам будет использовать какой-то буфер, и в этом случае BufferedInputStream будет бесполезным накладными расходами. –

0

Это также зависит от того, как вы собираетесь читать из InputStream. Если вы собираетесь читать его символ/байт за раз (т. Е. Read()), то BufferedInputStream уменьшит ваши накладные расходы, пошаговое выполнение массового чтения от вашего имени. Если вы собираетесь читать его в массив 4k или 8k байт/char, блок за раз тогда BuffredInputStream, вероятно, вам не пригодится.

25

ли смысл в всегда обернуть InputStream как BufferedInputStream, когда я знаю, является ли данный InputStream нечто иное, чем буферное?

No.

Это имеет смысл, если вы, вероятно, выполнять множество мелких операций чтения (один байт или несколько байтов в то время), или если вы хотите использовать некоторые функции более высокого уровня предлагается посредством буферизованных API; например, метод BufferedReader.readLine().

Однако, если вы только собираетесь выполнить большой блок считывает с помощью read(byte[]) и/или read(byte[], int, int) методов, обертывания InputStream в BufferedInputStream не помогает.

(В ответ на комментарий Питера Тиллмана по его собственному ответу, блок чтения прецедентов определенно представляет более 0,1% использования классов InputStream !! Однако он прав в том смысле, что обычно это безвредный использовать буферный API, если вам это не нужно.)

2

Возможно, вам не всегда нужна буферизация, поэтому ответ будет «Нет», в некоторых случаях это просто накладные расходы.

Есть еще одна причина, по которой это «нет», и это может быть более серьезным. BufferedInputStream (или BufferedReader) может привести к непредсказуемым сбоям при использовании с сетевым сокетом, когда вы также включили тайм-аут в сокете. Тайм-аут может возникать при чтении пакета. Вы больше не сможете получить доступ к данным, которые были перенесены на эту точку, даже если вы знали, что существует некоторое ненулевое количество байтов (см. java.net.SocketTimeoutException, который является подклассом java.io.InterruptedIOException, поэтому имеется переменная bytesTransferred).

Если вам интересно, как может произойти тайм-аут сокета во время чтения, просто подумайте о вызове метода read(bytes[]) и исходного пакета, который содержит сообщение, которое было разделено, но один из частичных пакетов задерживается за пределами таймаута (или оставшаяся часть таймаута). Это может происходить чаще, когда снова завертывается в то, что реализует java.io.DataInput (любое из чисел чтения для нескольких байтовых значений, например readLong() или readFully() или BufferedReader.readLine().

Обратите внимание, что java.io.DataInputStream также является плохим кандидатом на потоки сокетов, у которых есть тайм-аут, так как он не ведет себя хорошо с исключениями тайм-аута.

+1

Что касается BufferedInputStream и BufferedReader, это городской миф. Если вы получили таймаут чтения, (i) вы читаете, ergo внутренний буфер был пуст, иначе вы не читали; (ii) никакие данные не поступали в течение периода ожидания. Ergo данных не теряется. Попробуйте. – EJP

+0

@ EJP: Ты меня задумался, но я все еще думаю, что это может быть проблемой. Когда буферизованный поток действительно должен выполнять ввод/вывод (заполнять буфер), тогда это точка, когда вы можете получить исключение тайм-аута и внутренние переменные для отслеживания того, сколько байтов в буфере не будет обновлено. Я попытался проверить это, но, хотя я могу реплицировать исключения тайм-аута, я, похоже, не могу реплицировать ситуацию, когда 'bytesTransferred' отличен от нуля. До тех пор я не могу доказать это так или иначе. [Я потерял данные с DataInputStream и таймаутом.] –

+0

@EJP: Возможно, тогда чтение байтового массива из сокета никогда не приведет к частичному буферу чтения из-за таймаута, а затем 'bytesTransferred' никогда не будет отличным от нуля (иначе BufferredInputStream будет потерпеть неудачу). Это может быть также случай, когда реализация JVM на разных платформах/поставщиках может давать разные результаты - я только что тестировал в Windows 7 с помощью Sun/Oracle Java. –

Смежные вопросы