Почему java.io.Reader # skip реализован так, как он есть? - PullRequest
2 голосов
/ 03 июня 2011

Я все еще изучаю объектно-ориентированное программирование на Java. Я смотрел на реализацию Java java.io.Reader.skip, и мне интересно, почему именно она реализована так, как она есть. В частности, у меня есть вопросы об этих вещах, которые я заметил:

  1. Буфер, используемый для skip(long), является полем объекта Reader, а не нормальной переменной в методе.
  2. Максимальная длина буфера намного меньше, чем Integer.MAX_VALUE 2147,483,647. В частности, реализация Java использует 8192.
  3. java.io.InputStream реализует пропустить точно так же.

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

Длина буфера меньше, я думаю, что это связано с тем, чтобы считыватель блокировался на более короткие периоды, но поскольку считыватель синхронизирован, будет ли это иметь значение?

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

Подводя итог, я могу задать следующие вопросы: о том, сколько различий в скорости в среднем имеет использование поля, а не переменной для символьных массивов? Разве это не то же самое, что использовать Integer.MAX_VALUE в качестве максимальной длины буфера? И разве не лучше и проще использовать метод без параметров read в цикле for для байтовых потоков, поскольку другие методы read просто вызывают параметр без параметров read?

Извините, если мой вопрос странный, но я думаю, что благодаря этому вопросу я могу многое узнать об объектно-ориентированном программировании.

Ответы [ 4 ]

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

Сколько в среднем различий в скорости имеет использование поля, а не переменной для символьных массивов?

Это определенно варьируется от JVM до JVM, номногократное выделение массива 8K, вероятно, не так дешево, как его хранение.Конечно, скрытый урок здесь состоит в том, что не следует держаться за читателей, даже за закрытых, потому что они несут штраф в 8K.максимальная длина буфера?

Буфер должен быть предварительно выделен, а выделение массива в 2 Гб выглядит излишним.Помните, что причиной подкачки является амортизация стоимости вызова чтения - который иногда превращается в собственные операции.

Разве не лучше и проще использовать метод чтения без параметров вцикл for для байтовых потоков, поскольку другие методы чтения просто вызывают чтение без параметров?

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

Наконец, имейте в виду, что классы java.io имеют много-много недостатков, поэтому не думайте, что все, что есть, есть веские причины.

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

Чтение по одному символу за раз было бы намного менее эффективным - вы пропускали бы один вызов метода на байт, что обычно плохо при больших пропусках (много накладных расходов).

Размер чистого буфера прост в ответе: вы действительно хотите выделить Integer.MAX_VALUE блок RAM , если вы собираетесь пропустить 2G из файла?

Что касается точного размера и того, использовать ли экземпляр varialbe или нет, это компромисс, зависящий от реализации. Вы читаете реализацию, которая выбрала 8192 участника. Некоторые реализации имеют меньшие локальные реализации (512 можно увидеть здесь ).

Ничто в стандарте не требует каких-либо подробностей реализации, поэтому не полагайтесь на них вообще.

Если вы планируете делать что-то подобное, оцените различные подходы и выберите лучший компромисс в ваших конкретных обстоятельствах.

1 голос
/ 03 июня 2011

Для InputStream у вас часто есть подклассы, которые позволяют гораздо более эффективный пропуск, и они соответствующим образом переопределяют метод skip. Но для тех подклассов, которые не имеют эффективного способа пропуска (например, сжатие или распаковка входного потока), метод skip реализован на основе чтения, поэтому не каждый подкласс должен делать то же самое.

Существует несколько стратегий, как реализовать это в пакете java.io:

  • Пропуск базового потока:

    • FilterInputStream.skip() просто делегирует исходному потоку. Я не совсем уверен, насколько это полезно.
    • DataInputStream не переопределяет skip, но имеет другой метод с именем skipBytes(), который делает то же самое (хотя только для аргументов int). Он делегирует исходному исходному потоку. * BufferedInputStream.skip() переопределяет это, пропуская сначала существующее содержимое в своем собственном буфере, затем вызывая skip() в базовом потоке (если не установлено mark() - если есть метка, он должен прочитать все в буфер для поддержки reset()).
    • PushbackInputStream.skip сначала пропускает свой буфер возврата, а затем вызывает super.skip (который является FilterInputStream.skip (), см. Выше).
  • сброс индекса:

    • ByteArrayInputStream может тривиально поддерживать пропуск, просто устанавливая положение, где читать дальше.
    • StringBufferInputStream (который является устаревшей версией StringReader) поддерживает пропуск просто путем сброса индекса.
  • родная магия:

    • FileInputStream имеет skip как собственный метод. Я думаю, что это будет канонический пример, где он наиболее полезен.
  • прочитайте все и выбросьте:

    • LineNumberInputStream.skip() должен прочитать все, чтобы сосчитать строки. (Я не знал, что этот класс существует. Используйте взамен `LineNumberReader.)
    • ObjectInputStream не переопределяет skip, но имеет другой метод с именем skipBytes(), который делает то же самое (хотя только для int аргументов). Он делегирует внутренний класс (BlockDataInputStream.skip), который, в свою очередь, читает из базового потока, соблюдая протокол объектного потока для данных блока.
    • и, как сказано, реализация по умолчанию в InputStream. Это также используется SequenceInputStream и PipedInputStream.

Давайте посмотрим на Reader классы. В принципе, применяются те же стратегии.

  • пропустить базовый считыватель / поток:

    • FilterReader.skip() делает это.
    • PushBackReader сначала пропускает собственный буфер возврата, затем базовый считыватель.
  • сбросить индекс:

    • StringReader (этот на самом деле поддерживает пропуск назад)
    • CharArrayReader
  • прочитайте все и выбросьте:

    • по умолчанию Reader.skip(), который также используется PipedReader.
      • Для InputStreamReader подход "просто пропустить основной поток" будет работать только для кодировок с фиксированным числом байтов (т. Е. Серии ISO-8859, UTF-16 и некоторых аналогичных), а не для UTF-8, UTF -32 или другие кодировки с переменным числом байтов на символ, так как там мы должны прочитать все байты, чтобы узнать, сколько символов они представляют на самом деле. Это также относится к его подклассу FileReader.
    • BufferedReader (он не вызывает свой собственный read, но заполняет свой внутренний буфер, который читает из основного потока).
    • LineNumberReader (он должен делать это для отслеживания номеров строк)
1 голос
/ 03 июня 2011

вы забываете, что буфер 2 ^ 31 - 1 - это 2 ГБ памяти, которая должна быть выделена и затем не может использоваться ни для чего другого

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

. Максимальный буфер памяти в 8 КБ - намного лучшая альтернатива и лучший компромисс, поскольку он будет выделен только один раз (он будет использоваться повторно при каждой операции пропуска)

btw в java.io.InputStream пропуск пропуска является статическим и только когда-либо выделяется один раз, но поскольку нет чтения из него (он просто используется как память только для записи), естьне нужно беспокоиться о гонках

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...