Kotlin如何实现文件读写 存储Data Class分组数据
Kotlin实现类C++结构体单文件存储的可行方案
首先明确核心差异:C里的POD结构体是连续紧凑的内存布局,没有额外元数据,所以可以直接通过fwrite/fread拷贝内存块完成读写;但JVM平台上的data class是带对象头的堆对象,字段引用不连续、内存位置会被GC调整,不存在直接拷贝对象内存落盘的可能,不过有两个体验和C写法非常接近的方案,完全能满足你做简易单文件数据库的需求。
方案1:手动二进制序列化(最贴近C++原生操作习惯)
逻辑和你写C结构体读写完全一致:提前约定好每个字段的写入顺序、字节长度,写文件时按顺序把字段转成基础字节流追加写入,读文件时按相同规则逐段解析字节,组装成data class实例即可,文件结构完全可控,支持按偏移量随机读取,性能和C原生读写几乎没有差距。
示例代码如下,用JDK自带的流组件实现,不需要额外依赖:
import java.io.* // 示例存储的数据类 data class User( val id: Int, val name: String, val age: Short ) // 追加单条数据到文件,等价C++中 fwrite(&user, sizeof(User), 1, fp) fun User.writeAppend(path: String) { DataOutputStream(FileOutputStream(path, true)).use { out -> // 严格按约定顺序写字段,和C++结构体字段顺序要求一致 out.writeInt(id) // 字符串先写字节长度再写内容,避免读取错位 val nameBytes = name.toByteArray(Charsets.UTF_8) out.writeInt(nameBytes.size) out.write(nameBytes) out.writeShort(age.toInt()) } } // 读取全量数据为List,等价C++中一次性fread到结构体数组 fun readAllUser(path: String): List<User> { val result = mutableListOf<User>() DataInputStream(FileInputStream(path)).use { input -> // 循环读到文件末尾,对应C++ while(fread(...) == 1) 的逻辑 while (input.available() > 0) { val id = input.readInt() val nameLen = input.readInt() val nameBytes = ByteArray(nameLen) input.readFully(nameBytes) val name = String(nameBytes, Charsets.UTF_8) val age = input.readShort() result.add(User(id, name, age)) } } return result }
使用注意点:
- 读写的字段顺序、类型必须严格对应,和C++结构体要求内存对齐、字段顺序不能乱的规则完全一致
- 存在嵌套data class字段时,把嵌套对象按相同规则拆成基础字段逐段写入即可,和C++结构体嵌套的逻辑一致
- 单条数据长度固定时,可以直接通过
skipBytes跳转到指定偏移量读取单条记录,不需要全量加载,做简易索引非常方便
方案2:使用二进制序列化框架(代码量更少,维护成本更低)
如果觉得手动逐字段写读写逻辑太繁琐,可以用Kotlin官方的二进制序列化组件,给data class加上标记后,编译器会自动生成字段的序列化/反序列化逻辑,不需要手动处理每个字段的转换,哪怕后续修改data class的字段、新增嵌套对象,也不需要调整读写代码,开发效率更高。
示例代码如下:
import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.protobuf.ProtoBuf import java.io.* // 加序列化标记后自动生成转换逻辑 @Serializable data class User( val id: Int, val name: String, val age: Short ) // 追加写入单条数据 fun User.writeAppend(path: String) { // 自动把data class转成紧凑二进制字节数组 val dataBytes = ProtoBuf.encodeToByteArray(this) DataOutputStream(FileOutputStream(path, true)).use { out -> // 先写单条数据的字节长度,再写内容,避免读取错位 out.writeInt(dataBytes.size) out.write(dataBytes) } } // 读取全量数据为List fun readAllUser(path: String): List<User> { val result = mutableListOf<User>() DataInputStream(FileInputStream(path)).use { input -> while (input.available() > 0) { val len = input.readInt() val bytes = ByteArray(len) input.readFully(bytes) // 自动把字节数组转回data class实例 result.add(ProtoBuf.decodeFromByteArray(bytes)) } } return result }
使用注意点:
- 不要用JSON这类文本序列化格式做这类存储,体积大、性能差,二进制序列化格式的性能和文件体积和C++直接写结构体的效果基本持平
- 两种方案都支持增量追加写入,不需要每次修改全量重写文件,完全满足简易类数据库的使用需求
内容的提问来源于stack exchange,提问作者The Computer Genius
相关产品推荐
相关产品推荐

