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 是一个轻量级的文本解析工具,擅长按分隔符拆分输入,并支持将字符串自动转换为基本类型(如 intlong 等),非常适合读取格式化文本(比如日志、配置、数据文件)。

初始化方式如下:

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 后仍全加载)
  • 可结合 filtermap 等操作进行链式处理

❌ 缺点:

  • 必须用 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)

合理选择工具,少走弯路,才是高级开发的正确姿势 💪


原始标题:Read a File into an ArrayList | Baeldung