1. 概述

开发中经常需要将内存中的对象写入文件,或从文件读取内容到对象。处理基本类型和字符串很简单,但遇到数据结构和对象就复杂了。

Java 中常用的 HashMap 就是个典型例子。本文介绍三种处理 HashMap 文件读写的方法:Java Properties 类、对象序列化、以及第三方库的 JSON 序列化

2. 使用 Java Properties 类

属性文件是 HashMap 的常见应用场景,它存储字符串键值对作为应用配置。Java 的 Properties 类特别适合处理字符串类型的 HashMap。比如创建学生数据映射:

Map<String, String> studentData = new HashMap<>();
studentData.put("student.firstName", "Henry");
studentData.put("student.lastName", "Winter");

Properties 类实现了 Map<Object, Object>,所以能轻松接收 HashMap 的所有值:

Properties props = new Properties();
props.putAll(studentData);

创建临时文件并用 store 方法写入:

File file = File.createTempFile("student", ".data");
try (OutputStream output = Files.newOutputStream(file.toPath())) {
    props.store(output, null);
}

此方法接收 OutputStream(或 Writer)和可选的注释字符串(这里传 null)。生成的文件内容如下:

student.firstName: Henry
student.lastName: Winter

读取文件回 Properties 对象使用 load 方法:

Properties propsFromFile = new Properties();
try (InputStream input = Files.newInputStream(file.toPath())) {
    propsFromFile.load(input);
}

由于 Properties 实际只包含字符串键值对,可通过流式处理还原原始 HashMap

HashMap<String, String> studentDataFromProps = propsFromFile.stringPropertyNames()
  .stream()
  .collect(Collectors.toMap(key -> key, props::getProperty));
assertThat(studentDataFromProps).isEqualTo(studentData);

优点:简单直接
限制:仅适用于字符串键值对的 HashMap

3. 对象序列化方案

Java 提供了 Serializable 接口实现对象与字节流的转换。先定义可序列化的 Student 类(按规范设置 serialVersionUID):

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    private String firstName;
    private String lastName;

    // 标准getter/setter/equals/hashCode方法
}

创建学号到学生对象的映射:

Map<Integer, Student> studentData = new HashMap<>();
studentData.put(1234, new Student("Henry", "Winter"));
studentData.put(5678, new Student("Richard", "Papen"));

ObjectOutputStream 写入文件:

File file = File.createTempFile("student", ".data");
try (FileOutputStream fileOutput = new FileOutputStream(file); 
  ObjectOutputStream objectStream = new ObjectOutputStream(fileOutput)) {
    objectStream.writeObject(studentData);
}

⚠️ 注意:生成的是二进制文件,人类不可读。验证文件正确性可用 ObjectInputStream 读取:

Map<Integer, Student> studentsFromFile;
try (FileInputStream fileReader = new FileInputStream(file);
  ObjectInputStream objectStream = new ObjectInputStream(fileReader)) {
    studentsFromFile = (HashMap<Integer, Student>) objectStream.readObject();
}
assertThat(studentsFromFile).isEqualTo(studentData);

⚠️ 踩坑点:需要强制类型转换,且类型安全需自行保障(示例中忽略未检查转换警告)

优点:核心Java特性,灵活性高
限制:类必须实现 Serializable,且无法修改第三方类时不可用

4. JSON 库方案

JSON 是通用的键值数据格式,人类可读性强。学生数据的 JSON 示例:

{
    1234: {
        "firstName": "Henry",
        "lastName": "Winter"
    },
    5678: {
        "firstName": "Richard",
        "lastName": "Papen"
    }
}

下面用 Jackson 和 Gson 两个主流库实现 JSON 序列化。

4.1. Jackson 方案

Jackson 是 Java 中常用的 JSON 序列化库。创建 ObjectMapper 写入文件:

ObjectMapper mapper = new ObjectMapper();
File file = File.createTempFile("student", ".data");
try (FileOutputStream fileOutput = new FileOutputStream(file)) {
    mapper.writeValue(fileOutput, studentData);
}

读取文件回 HashMap 需要类型信息:

Map<Integer, Student> mapFromFile;
try (FileInputStream fileInput = new FileInputStream(file)) {
    TypeReference<HashMap<Integer, Student>> mapType 
      = new TypeReference<HashMap<Integer, Student>>() {};
    mapFromFile = mapper.readValue(fileInput, mapType);
}
assertThat(mapFromFile).isEqualTo(studentData);

⚠️ 关键点:通过 TypeReference 指定泛型类型,否则无法正确反序列化。

优点:无需修改类定义(可移除 Serializable
限制:要求类有无参构造函数

4.2. Gson 方案

Gson 是另一个流行的 JSON 库。序列化代码:

Gson gson = new Gson();
File file = File.createTempFile("student", ".data");
try (FileWriter writer = new FileWriter(file)) {
    gson.toJson(studentData, writer);
}

反序列化使用 TypeToken 提供类型信息:

Map<Integer, Student> studentsFromFile;
try (FileReader reader = new FileReader(file)) {
    Type mapType = new TypeToken<HashMap<Integer, Student>>() {}.getType();
    studentsFromFile = gson.fromJson(reader, mapType);
}
assertThat(studentsFromFile).isEqualTo(studentData);

优点:API 简洁,提供 InstanceCreator 接口处理无参构造问题
限制:同样依赖无参构造函数(但有补救方案)

5. 方案总结

三种 HashMap 文件读写方案对比:

场景 推荐方案 关键优势 主要限制
纯字符串键值对 Properties 简单直接 仅支持字符串
可修改的自定义类 对象序列化 核心Java特性 需实现 Serializable
不可修改类/可读性需求 JSON 库(Jackson/Gson) 人类可读/无需修改类 依赖无参构造

选择建议

  • 配置文件场景 → Properties
  • 完全可控的内部类 → 对象序列化
  • 第三方类/需要可读性 → JSON 库

所有示例代码可在 GitHub 获取。


原始标题:How to Write and Read a File with a Java HashMap

« 上一篇: Java Vector 类详解
» 下一篇: Round the Date in Java