1. 概述
本文将系统性地介绍 在 Java 中将文件内容读取到 ArrayList
的几种常用方法。
Java 提供了丰富的文件读取手段。一旦文件被加载进内存,我们就可以对其进行各种操作,比如排序、过滤、搜索等。而这些操作往往需要将整个文件内容加载到内存中的集合(如 List
或数组)中,才能高效处理。
因此,如何把一个文件按行(或按词)读成一个 ArrayList<String>
,是日常开发中非常基础但高频的需求。下面从原生 API 到第三方库,逐一盘点主流做法,帮你避开踩坑,写出更简洁、高效的代码 ✅
2. 使用 FileReader
最原始的方式就是直接使用 FileReader
。顾名思义,它是专门用于从文件中读取字符流的便利类。
FileReader
提供了多个构造函数:
FileReader f = new FileReader(String filepath);
FileReader f = new FileReader(File f);
FileReader f = new FileReader(FileDescriptor fd);
这些构造函数都默认使用系统平台的字符编码和缓冲区大小。如果你需要指定编码(如 UTF-8),建议配合 InputStreamReader
使用,否则容易乱码 ❌
下面是一个用 FileReader
手动逐字符读取并按换行符分割成 ArrayList
的示例:
ArrayList<String> result = new ArrayList<>();
try (FileReader f = new FileReader(filename)) {
StringBuffer sb = new StringBuffer();
while (f.ready()) {
char c = (char) f.read();
if (c == '\n') {
result.add(sb.toString());
sb = new StringBuffer();
} else {
sb.append(c);
}
}
if (sb.length() > 0) {
result.add(sb.toString());
}
}
return result;
⚠️ 踩坑提示:这种方式效率极低,因为每次 read()
只读一个字符,I/O 调用过于频繁。而且 ready()
并不能完全保证后续读取不会阻塞。不推荐用于实际项目。
3. 使用 BufferedReader
更合理的方式是使用 BufferedReader
包装 FileReader
。它的核心优势在于 自带缓冲机制,能批量读取字符,大幅减少底层 I/O 调用次数,性能提升明显 ✅
BufferedReader
构造器接收一个 Reader
类型参数,通常传入 FileReader
:
BufferedReader br = new BufferedReader(new FileReader(filename));
BufferedReader br = new BufferedReader(new FileReader(filename), size); // 可自定义缓冲区大小
最关键的是,它提供了 readLine()
方法,可以直接读取一整行字符串,省去了手动拼接字符的麻烦。
代码实现如下:
ArrayList<String> result = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
while (br.ready()) {
result.add(br.readLine());
}
}
⚠️ 注意:ready()
判断的是“是否 ready to be read”,但某些场景下可能不准。更稳妥的做法是判断 readLine()
是否返回 null
:
String line;
while ((line = br.readLine()) != null) {
result.add(line);
}
✅ 推荐指数:⭐⭐⭐⭐
适合需要精细控制读取过程、或处理超大文件时逐行处理的场景。
4. 使用 Scanner
Scanner
是一个轻量级的文本解析工具,擅长按分隔符拆分输入,并支持将字符串自动转换为基本类型(如 int
、long
等),非常适合读取格式化文本(比如日志、配置、数据文件)。
初始化方式如下:
Scanner s = new Scanner(new File(filename));
Scanner s = new Scanner(new FileReader(filename));
它也提供了 nextLine()
方法来读取整行内容,配合 hasNext()
可以安全遍历:
ArrayList<String> result = new ArrayList<>();
try (Scanner s = new Scanner(new FileReader(filename))) {
while (s.hasNext()) {
result.add(s.nextLine());
}
return result;
}
此外,如果你要读的是纯数字列表(比如每行一个整数),可以直接用 nextInt()
:
ArrayList<Integer> result = new ArrayList<>();
try (Scanner s = new Scanner(new FileReader(filename))) {
while (s.hasNext()) {
result.add(s.nextInt());
}
return result;
}
✅ 优势:语法简洁,类型自动转换,适合结构化文本。
❌ 劣势:默认以空白符为分隔符,若需读完整行建议用 nextLine()
;性能略低于 BufferedReader
。
5. 使用 Files.readAllLines()
Java 7 引入的 java.nio.file.Files
类提供了一个极其简单粗暴的方法:readAllLines()
。一行代码搞定文件到 List 的转换,堪称最简洁方案 ✅
List<String> result = Files.readAllLines(Paths.get(filename));
如果文件使用非默认编码(如 ISO-8859-1 或 GBK),可以显式指定:
Charset charset = Charset.forName("ISO-8859-1");
List<String> result = Files.readAllLines(Paths.get(filename), charset);
⚠️ 注意事项:
- 返回的是
List<String>
,不是ArrayList
,如需ArrayList
需手动转换 - 会一次性将整个文件加载进内存,不适合超大文件(>100MB),否则容易 OOM ❌
✅ 推荐指数:⭐⭐⭐⭐⭐(小文件首选)
6. 使用 Files.lines()
同样是 Files
类提供的方法,lines()
返回的是一个 Stream<String>
,更适合函数式编程风格。
使用方式如下:
try (Stream<String> lines = Files.lines(Paths.get(filename))) {
return lines.collect(Collectors.toCollection(ArrayList::new));
}
✅ 优势:
- 返回的是流,支持懒加载,内存友好(虽然 collect 后仍全加载)
- 可结合
filter
、map
等操作进行链式处理
❌ 缺点:
- 必须用 try-with-resources 包装,否则流不会自动关闭
- 如果只是简单读取所有行,不如
readAllLines()
直观
✅ 适用场景:需要对文件内容做流式处理、过滤、映射等操作时非常方便。
7. 使用 Apache Commons IO
第三方库 Apache Commons IO 提供了大量实用的 I/O 工具类,其中 FileUtils.readLines()
方法可以直接将文件读成 List<String>
。
首先引入依赖:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
使用示例:
List<String> result = FileUtils.readLines(new File(filename), "utf-8");
return new ArrayList<>(result);
✅ 优点:
- API 极其简洁
- 自动处理资源释放,无需 try-with-resources
- 支持指定编码
⚠️ 注意:返回的是 List
,通常为 ArrayList
,但不保证,建议用 new ArrayList<>(...)
显式转换。
✅ 推荐在项目已引入 Commons IO 时使用,省事高效。
8. 使用 Guava
Google 的 Guava 库同样提供了便捷的文件读取工具。其 Files.readLines()
方法也非常直观。
添加依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.3.0-jre</version>
</dependency>
代码示例:
List<String> result = Files.readLines(new File(filename), Charset.forName("utf-8"));
return new ArrayList<>(result);
⚠️ 注意:Guava 的 Files
类在 com.google.common.io.Files
,不要导错包!
✅ 优点:API 简洁,与 Guava 其他工具无缝集成
❌ 缺点:引入整个 Guava 库只为读文件,略显重(除非你已经在用 Guava)
9. 总结与建议
方法 | 适用场景 | 是否推荐 |
---|---|---|
FileReader + 手动读取 |
仅学习用 | ❌ |
BufferedReader |
需要逐行控制、大文件处理 | ✅✅✅ |
Scanner |
读取结构化数据、基本类型 | ✅✅ |
Files.readAllLines() |
小文件,快速开发 | ✅✅✅✅✅ |
Files.lines() |
流式处理、函数式风格 | ✅✅✅ |
Apache Commons IO | 项目已引入 Commons IO | ✅✅✅✅ |
Guava | 项目已使用 Guava | ✅✅✅ |
📌 最终建议:
- 小文件 + 快速开发 → 用
Files.readAllLines()
- 大文件或需流式处理 → 用
Files.lines()
或BufferedReader
- 格式化文本(如数字列表)→ 用
Scanner
- 已用 Commons IO 或 Guava → 直接调对应工具类
所有示例代码均已整理至 GitHub 仓库:https://github.com/baeldung/tutorials(模块:core-java-modules/core-java-io-7)
合理选择工具,少走弯路,才是高级开发的正确姿势 💪