Как StringBuilder можно использовать для чтения больших текстовых файлов на Java?

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

Почти каждая программа, с которой я столкнулся, использует String для чтения текстовых файлов, но Java резервирует место для каждого строкового литерала. Вот почему я думаю, что использование памяти увеличивается, поскольку все объекты String сохраняются. Все классы java.io имеют дело со String. Но если мы не используем StringBuilder, как мы можем уменьшить использование памяти?

В конце концов, сокращение использования памяти является основной задачей StringBuilder [поскольку он не является неизменным, как String]. Тогда как мы можем использовать эту функцию в операции ввода-вывода Java без использования String, т.е. без использования чего-то вроде этого: sb.append ([String object]);


person Debadyuti Maiti    schedule 24.03.2012    source источник
comment
Обычно секрет обработки больших файлов заключается не в попытке полностью прочитать их в памяти, и для этого вам не нужен StringBuilder.   -  person Joni    schedule 24.03.2012
comment
Что за беспокойство. Вы пытаетесь прочитать весь файл в виде строки?   -  person ring bearer    schedule 24.03.2012
comment
Строковые литералы не имеют ничего общего с файловым вводом-выводом   -  person josefx    schedule 24.03.2012
comment
Для меня этот вопрос не имеет никакого смысла. Если вы хотите сохранить файл в памяти, вам придется заплатить за это цену, независимо от языка и / или среды выполнения, которую вы используете. Вы должны привести конкретный пример.   -  person home    schedule 24.03.2012
comment
Хм, немного знаний - вещь опасная.   -  person Kirk Woll    schedule 24.03.2012
comment
@DaveNewton Каждый раз, когда мы выполняем br.readLine (), поскольку он возвращает String, no. временных объектов String [которые хранятся в пуле констант String JVM] в памяти увеличивается. Этого я хотел избежать и найти решение с помощью StringBuilder.   -  person Debadyuti Maiti    schedule 25.03.2012
comment
Это не константа, если это не константа.   -  person Dave Newton    schedule 25.03.2012
comment
@DaveNewton Но String неизменяем, поэтому есть шанс создать множество временных объектов String.   -  person Debadyuti Maiti    schedule 25.03.2012
comment
Это отличается от использования пула строковых констант. Если вы не хотите использовать строки, используйте байтовые буферы. Вы действительно профилировали что-нибудь, чтобы увидеть, заботитесь ли вы об относительно небольших улучшениях производительности / памяти, которые вы сделаете?   -  person Dave Newton    schedule 25.03.2012
comment
@DaveNewton Что ж, согласно Кэти Сьерра [в книге SCJP], всякий раз, когда мы пытаемся сделать что-то вроде этого: String s = new String (abc); // создает два объекта, // и одну ссылочную переменную. В этом случае, поскольку мы использовали ключевое слово new, Java создаст новый объект String в обычной (не пуловой) памяти, и s будет ссылаться на него. Кроме того, в пул будет помещен буквальный abc. Это то, что я хотел указать, т.е. для каждой String всегда есть один повторяющийся литерал, в отличие от StringBuilder.   -  person Debadyuti Maiti    schedule 25.03.2012
comment
Вы не создаете строковый литерал, когда читаете из файла, потому что там нет литерала - я не уверен, почему вы этого не видите. Для строкового литерала должен быть литерал - в вашем примере есть - это "abc". Вы не делаете этого, когда читаете из файла.   -  person Dave Newton    schedule 25.03.2012
comment
@DaveNewton, хорошо. Теперь я понял. Это фрагмент кода метода StringBuilder readline (). : String str; if (s == null) {str = new String (cb, startChar, i - startChar); } else {s.append (cb, startChar, i - startChar); стр = s.toString (); } ... str возвращается из этого метода. Итак, если я прав, здесь фактически строковый литерал не создается при выполнении str = new String (cb, startChar, i - startChar); или метод StringBuffer toString (). Правильно?   -  person Debadyuti Maiti    schedule 25.03.2012


Ответы (6)


Предположим, у вас есть n строк, каждая длиной 1, которые вы читаете из своего ввода - для простоты.

Использование operator+ в строках при чтении будет создавать объект String каждый раз, когда вы объединяете строки, поэтому вы получаете строки длиной 1,2,3, ..., n

Таким образом, общее использование памяти строк составляет 1 + 2 + .. + n = O(n^2) в дополнение к n строкам, которые вы читаете из ввода.

в то время как, если вы используете StringBuilder для создания последней строки, вы фактически создаете n - для ввода [каждый длиной 1] и один объект для последней строки - размером n, поэтому общее использование памяти 1 + 1 + .. + 1 + n = O(n)

Таким образом, даже если вы используете sb.append(String) - использование пространства асимптотически больше, чем при создании всех промежуточных строк - поскольку вам не нужно создавать промежуточные объекты String.

Вдобавок - производительность [время] должна быть лучше при использовании StringBuilder - как из-за того, что вы создаете меньше объектов, так и из-за меньшего использования памяти - gc не нужно работать так усердно, как при наивном конкатенации строк.

(*) Обратите внимание, что легко видеть, что вышесказанное по-прежнему справедливо для строк любой длины.

person amit    schedule 24.03.2012

Вы можете использовать метод добавления символов StringBuilders, чтобы избежать создания промежуточных строк, посмотрите этот пост: https://stackoverflow.com/a/9849624/102483 Имейте в виду, что нет способа уменьшить объем памяти, занимаемый последней строкой, так, чтобы он был меньше размера файла, который вы читаете.

person Hiro2k    schedule 24.03.2012

В зависимости от того, что вы делаете, вы можете создать пул объектов String и / или StringBuilder, которые загружаются с нужными вам значениями, очищаются и затем используются повторно. Вы можете настроить пул так, чтобы он увеличивался до максимального значения, и, если объекты в пуле не используются, установите для них значение NULL, где они в конечном итоге будут возвращены сборщиком мусора.

person jhenderson2099    schedule 24.03.2012

Возможно, вы захотите рассмотреть что-то вроде этого:

  BufferedReader reader = 
    new BufferedReader(
      new InputStreamReader(
        new ByteArrayInputStream(data)));
  String line;

  while ((line = reader.readLine()) != null)
    ...

Смотрите эти ссылки для более подробной информации:

BufferedReader для большого ByteBuffer?

http://www.tutorialspoint.com/java/java_bytearrayinputstream.htm

person paulsm4    schedule 24.03.2012
comment
помещение этого в StringBuilder вызывает ошибку OutOfMemory на android для строк, составляющих всего 2,5% от общего объема памяти. - person Michael; 30.12.2014

Reader и его подклассы основаны на символах и char [], String используют только удобные методы. Поскольку StringBuilder.append () принимает char [], вы можете избежать создания ненужных объектов String, если используете только методы, построенные вокруг char [].

Обратите внимание, что хотя это уменьшает количество временно создаваемых объектов String, общие требования к памяти остаются неизменными, но сборщик пакетов будет собирать любые созданные в противном случае String.

person josefx    schedule 24.03.2012

Вместо String попробуйте использовать StringBuilder для добавления данных, прочитанных из файла. Если вы используете String, вы можете создать в памяти несколько строковых объектов.

person Pramod    schedule 24.03.2012