不好用非常大的字符串? (JAVA)

创建大字符串是否有任何负面影响? 例如,如果我们从一个可能很大的文本文件中读取文本:

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

逐行处理文件(通常)是一个更好的解决方案,为什么?

谢谢

流媒体与否

当你可以流式传输时,你可以处理任何大小的文件(假设你真的可以忘记你已经看过的所有数据)。 你最终会得到一个自然的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"); } } 

我相信每次执行+ =时都会创建一个新的String对象。 请改用StringBuilder

使用StringBuilder。 您的方法是创建可能的数千个丢弃对象。 字符串是不可变对象,这意味着一旦创建了一个就无法更改它……您只能创建一个新的String并将引用分配给当前实例。 StringBuilder在速度和内存方面将有数百甚至数千倍的效率。

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

然而,大多数Java编译器现在都会为您优化,但是提前编写代码是一种很好的做法。

正如Jon Skeet所说,流媒体是一种更强大的数据处理方式。 此外,字符串具有有限大小的Max_INT字符 – 因此,如果您的文件可能比这大,那么您应该考虑尽可能地处理数据流。

如果输入大于系统内存(例如,输入是由另一台计算机通过HTTP连接生成的),该怎么办? 如果您一次处理一行,那么您总是在进步,并且最终会处理整个输入,假设输入是有限的。 但是,如果您等待查看整个输入,则在执行任何处理之前,您将耗尽内存并中断。

通常,以流方式处理数据是好的。 这也适用于使用迭代器而不是随机访问来执行处理。 它将允许您的程序扩展到非常大的输入大小,并且它还允许您的程序被流水线化(即,另一个程序可以开始处理您的程序输出,而您的程序仍在处理其自己的输入)。 在许多不同计算机之间的大型媒体传输的这个时代,这几乎总是一个支持的好主意。

一些额外的要点:

  1. 如果您将大量数据读入StringBuilder然后调用toString()则JVM将在转换期间暂时需要两倍的char[]存储空间 。 如果您可以将数据作为CharSequenceStringBuilder实现CharSequence ),则可以避免这种情况。
  2. 如果确实需要将所有数据读入内存,则尝试的另一件事是将String表示为单词列表(即List )并在每个单词上调用intern() 。 如果数据包含大量重复的单词,则这将显着节省内存