将整个文件读入单个字符串与逐行读取相比有哪些优点和缺点?

What are the advantages and disadvantages of reading an entire file into a single String as opposed to reading it line by line?

具体来说,我的最终目标是将文件中的每个逗号分隔词存储在 List<String> 中,我想知道我应该采用哪种方法。

方法一:

String fileContents = new Scanner(new File("filepath")).useDelimiter("\Z").next();
List<String> list = Arrays.asList(fileContents.split("\s*,\s*"));

方法二:

Scanner s = new Scanner(new File("filepath")).useDelimiter(",");
List<String> list = new ArrayList<>();
while (s.hasNext()){
    list.add(s.next());
}
s.close();

方法一:

String 的最大大小限制,即最大长度的 String Integer.MAX_VALUE 是可能的,或者是运行时可能的最大数组

因此,如果它是一个非常大的文件,首选方法 2

方法#1 会将整个文件读入内存。这有几个与性能相关的问题:

  • 如果文件很大占用大量内存。
  • 由于Scanner.next()调用需要积累字符的方式,字符可能需要复制2次甚至3次。
  • 由于您将通用模式匹配引擎用于非常特定的目的,因此还有其他效率低下的问题。

方法 3(这是方法 1,文件读取做得更好)解决了很多效率问题,但您仍然将整个文件内容保存在内存中。

方法 #2 从内存使用的角度来看是最好的,因为您不会将整个文件内容保存为单个字符串或缓冲区1。性能也可能是最好的,因为(我的直觉说)这种方法至少避免了一个字符副本。

但是,如果这真的很重要,您应该对备选方案进行基准测试,同时牢记两点:

  • "Premature optimization" 通常是白费力气。 (或者换句话说,很可能这部分代码的性能真的无关紧要。性能瓶颈可能在其他地方。)
  • 编写 Java 基准有很多陷阱,可能导致虚假的性能指标和错误的结论。

另一件需要注意的事情是,您正在尝试做的事情(按顺序创建所有 "words" 的列表)无法缩放。对于足够大的输入文件,应用程序将 运行 超出堆 space。如果您预计 运行 在大于 100Mb 左右的输入文件上执行此操作,它可能会开始成为一个问题。

解决方案可能是将您的处理转换为更基于 "stream" 的处理...这样您就不需要在内存中拥有所有单词的列表。

这与方法 #1 的问题本质上是同一个问题。


1 - 除非文件很小并且适合缓冲区......然后整个问题基本上没有实际意义。

如果您在实际上不需要时将整个文件读入内存,那么您是:

  • 浪费时间:在您阅读整个文件之前不会处理任何内容
  • 消瘦space
  • 使用无法扩展到大文件的技术。

这样做没有什么值得推荐的。