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

如何使用Chronicle Map对long[]进行随机索引的序列化与反序列化?

解决Chronicle Map中short键+可变最大长度long数组值的序列化问题

嗨,作为Chronicle Map新手,碰到这种自定义值序列化的问题太正常了!我帮你拆解下键和值的处理方案,重点在值的自定义序列化上,因为每个Map的long数组最大长度不一样,得针对性处理~

一、short键的序列化:直接用内置支持

对于short类型的键,你根本不用自己写序列化器!Chronicle Map已经为基本类型的包装类(比如Short)提供了高效的内置KeyMarshaller,构建Map时直接指定键类型为Short.class就好,底层会自动完成序列化/反序列化,性能拉满还不用造轮子。

二、long数组值的自定义序列化:按Map的最大长度定制

这部分是核心,因为每个Map的long数组最大长度不同,而Chronicle Map的堆外存储要求值的序列化大小固定(这样才能预先分配内存空间)。所以我们需要为每个特定最大长度的Map,单独实现对应的ValueMarshaller<long[]>。

步骤1:实现自定义的固定长度long数组Marshaller

这个Marshaller要做三件事:

  1. 写入时先存实际数组长度,再写入元素(不足最大长度的补0,保证总字节数固定)
  2. 读取时先读实际长度,再读取对应数量的元素,跳过补0的字节
  3. 告诉Chronicle Map这个值的固定字节大小

代码示例如下:

import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.map.ValueMarshaller;

public class FixedLengthLongArrayMarshaller implements ValueMarshaller<long[]> {
    private final int maxArrayLength;

    // 构造时传入当前Map允许的数组最大长度
    public FixedLengthLongArrayMarshaller(int maxArrayLength) {
        this.maxArrayLength = maxArrayLength;
    }

    @Override
    public void write(Bytes out, long[] value) {
        // 先校验数组长度,避免超过当前Map的上限
        if (value.length > maxArrayLength) {
            throw new IllegalArgumentException(
                String.format("数组长度不能超过当前Map的最大值%s,当前传入%s", maxArrayLength, value.length)
            );
        }
        // 写入实际数组长度(4字节),反序列化时靠它知道要读多少元素
        out.writeInt(value.length);
        // 写入数组元素,不足最大长度的补0,保证总字节数固定
        for (int i = 0; i < maxArrayLength; i++) {
            out.writeLong(i < value.length ? value[i] : 0L);
        }
    }

    @Override
    public long[] read(Bytes in, long[] using) {
        int actualLength = in.readInt();
        // 复用传入的using数组(如果足够大),减少GC开销
        long[] result = (using != null && using.length >= actualLength) ? using : new long[actualLength];
        // 读取实际长度的元素
        for (int i = 0; i < actualLength; i++) {
            result[i] = in.readLong();
        }
        // 跳过剩下的补0字节,保证指针位置正确
        in.skip((long) (maxArrayLength - actualLength) * 8);
        return result;
    }

    @Override
    public long fixedSize() {
        // 固定字节数:4字节(实际长度) + 最大长度*8字节(每个long占8字节)
        return 4 + (long) maxArrayLength * 8;
    }
}

步骤2:为不同最大长度的Map构建实例

每个Map对应自己的Marshaller实例,传入对应的最大数组长度就行:

import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.map.ChronicleMapBuilder;

public class ChronicleMapExample {
    public static void main(String[] args) {
        // 第一个Map:允许的long数组最大长度为10
        int maxLen1 = 10;
        ChronicleMap<Short, long[]> map1 = ChronicleMapBuilder
                .of(Short.class, long[].class)
                .valueMarshaller(new FixedLengthLongArrayMarshaller(maxLen1))
                .entries(1000) // 根据你的业务需求设置预计的条目数
                .create();

        // 第二个Map:允许的long数组最大长度为20
        int maxLen2 = 20;
        ChronicleMap<Short, long[]> map2 = ChronicleMapBuilder
                .of(Short.class, long[].class)
                .valueMarshaller(new FixedLengthLongArrayMarshaller(maxLen2))
                .entries(1000)
                .create();

        // 测试写入读取
        map1.put((short) 1, new long[]{1L, 2L, 3L});
        long[] result = map1.get((short) 1);
        // 输出:[1, 2, 3]
        System.out.println(java.util.Arrays.toString(result));
    }
}

三、额外注意事项

  • 固定大小是核心:Chronicle Map的堆外存储依赖固定大小的键值,所以fixedSize()方法一定要返回正确的字节数,否则会出现内存 corruption 或者读取错误。
  • 校验数组长度:写入时一定要检查数组长度不超过当前Map的最大值,避免数据被截断或者破坏堆外结构。
  • 复用数组提升性能:read方法中的using参数是用来复用对象的,尽量传入已有的数组,减少垃圾回收的压力,尤其是高并发场景下。

内容的提问来源于stack exchange,提问作者junkie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:14:35