Плохо использовать очень большие строки? (Ява)

Есть ли какие-либо негативы для создания огромных строк? Например, если мы читаем текст из потенциально огромного текстового файла:

while (scanner.hasNext()) {
  someString += scanner.next();
}
// do something cool with someString

Будет ли обработка строки строки за строкой (как правило) лучшим решением и почему?

Спасибо

Ответ 1

Потоковая передача без

Когда вы можете передавать потоки, вы можете обрабатывать файлы любого размера (при условии, что вы действительно можете забыть все данные, которые вы уже видели). Вы получаете естественную сложность O (n), что очень хорошо. Вы не сломаетесь, исчерпав память.

Потоковая передача прекрасна... но не работает в каждом сценарии.

StringBuilder

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

Сначала результаты, затем код. Это очень грубый и готовый бенчмарк, но результаты достаточно драматичны, чтобы подчеркнуть...

c:\Users\Jon\Test>java Test slow
Building a string of length 120000 without StringBuilder took 21763ms

c:\Users\Jon\Test>java Test fast
Building a string of length 120000 with StringBuilder took 7ms

И код...

class FakeScanner
{
    private int linesLeft;
    private final String line;

    public FakeScanner(String line, int count)
    {
        linesLeft = count;
        this.line = line;
    }

    public boolean hasNext()
    {
        return linesLeft > 0;
    }

    public String next()
    {
        linesLeft--;
        return line;
    }
}

public class Test
{    
    public static void main(String[] args)
    {
        FakeScanner scanner = new FakeScanner("test", 30000);

        boolean useStringBuilder = "fast".equals(args[0]);

        // Accurate enough for this test
        long start = System.currentTimeMillis();

        String someString;
        if (useStringBuilder)
        {
            StringBuilder builder = new StringBuilder();
            while (scanner.hasNext())
            {
                builder.append(scanner.next());
            }
            someString = builder.toString();
        }
        else
        {
            someString = "";     
            while (scanner.hasNext())
            {
                someString += scanner.next();
            }        
        }
        long end = System.currentTimeMillis();

        System.out.println("Building a string of length " 
                           + someString.length()
                           + (useStringBuilder ? " with" : " without")
                           + " StringBuilder took " + (end - start) + "ms");
    }
}

Ответ 2

Я считаю, что создает новый объект String каждый раз, когда вы делаете + =. Вместо этого используйте StringBuilder.

Ответ 3

Используйте StringBuilder. Ваш подход создает потенциально тысячи отбрасываемых объектов. Строки являются неизменяемыми объектами, а это означает, что после его создания вы не можете его изменить... вы можете создать новую строку и назначить ссылку на ваш текущий экземпляр. StringBuilder будет сотен, если не тысячи раз более эффективными по скорости и памяти.

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/StringBuilder.html

Большинство компиляторов Java, однако, теперь оптимизируют для вас все, но это хорошая практика для правильного кодирования кода.

Ответ 4

Как сказал Джон Скит, потоковая передача - более надежный способ обработки данных. Кроме того, строки имеют конечный размер символов Max_INT, поэтому, если ваши файлы, вероятно, будут больше, вам следует рассмотреть возможность обработки потоковой передачи данных, если это вообще возможно.

Ответ 5

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

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

Ответ 6

Несколько дополнительных точек:

  • Если вы читаете очень большой объем данных в StringBuilder, а затем вызываете toString(), JVM временно потребует удвоить объем char[] пространства для хранения во время преобразования. Если вы можете обрабатывать данные как CharSequence (StringBuilder реализует CharSequence), вы можете избежать этого.
  • Еще одна вещь, которую вы пытаетесь, если вам нужно прочитать все данные в памяти, - это представить String как список слов (т.е. List<String>) и вызвать intern() для каждого слова. Если данные содержат большое количество повторяющихся слов, это будет означать значительную экономию памяти.