You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 13:57:19