有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何使用编年史映射在随机索引上使用get/set序列化/反序列化long[]值?

我不熟悉编年史地图。我正在尝试使用历史记录映射对堆外映射进行建模,其中键是一个短基元,值是一个长基元数组。已知给定映射的长数组值的最大大小。但是,我将有多个这种类型的映射,每个映射对于长数组值可能有不同的最大大小。我的问题涉及密钥和值的序列化/反序列化

通过阅读文档,我了解到对于键,我可以使用值类型ShortValue并重用该接口实现的实例。关于这个值,我发现这个页面讨论的是DataAccess and SizedReader,它给出了byte[]的一个例子,但我不确定如何将它适应于长[]。我还有一个额外的要求,就是我需要在长数组中的任意索引处获取和设置值,而无需每次完全序列化/反序列化整个值

因此,我的问题是:在构建映射时,如何对值类型进行建模?如果每个映射的最大大小已知,并且我需要能够读取和写入随机索引,而无需每次序列化/反序列化整个值负载,那么对于长[]数组,我需要什么序列化/反序列化代码?理想情况下,长[]将直接编码/解码到堆外或从堆外进行编码/解码,而无需进行堆内到字节[]的中间转换,并且历史记录映射代码也不会在运行时分配。多谢各位


共 (2) 个答案

  1. # 1 楼答案

    首先,我建议使用某种LongList接口抽象,而不是long[],这样可以更容易地处理大小变化,提供可选的flyweight实现,等等

    如果只想读/写大型列表中的单个元素,应该使用advanced contexts API

    /** This method is entirely garbage-free, deserialization-free, and thread-safe. */
    void putOneValue(ChronicleMap<ShortValue, LongList> map, ShortValue key, int index,
            long element) {
        if (index < 0) throw throw new IndexOutOfBoundsException(...);
        try (ExternalMapQueryContext<ShortValue, LongList, ?> c = map.getContext(key)) {
            c.writeLock().lock(); // (1)
            MapEntry<ShortValue, LongList> entry = c.entry();
            if (entry != null) {
                Data<LongList> value = entry.value();
                BytesStore valueBytes = (BytesStore) value.bytes(); // (2)
                long valueBytesOffset = value.offset();
                long valueBytesSize = value.size();
                int valueListSize = (int) (valueBytesSize / Long.BYTES); // (3)
                if (index >= valueListSize) throw new IndexOutOfBoundsException(...);
                valueBytes.writeLong(valueBytesOffset + ((long) index) * Long.BYTES,
                    element);
                ((ChecksumEntry) entry).updateChecksum(); // (4)
            } else {
                // there is no entry for the given key
                throw ...
            }
        }
    }
    

    注:

    1. 必须从一开始就获取writeLock(),否则在调用context.entry()方法时会自动获取readLock(),以后将无法将read lock升级为write lock。请仔细阅读^{} javadoc
    2. ^{}正式返回RandomDataInput,但您可以确定(它在Data.bytes()javadoc中指定)它实际上是^{}的一个实例(即RandomDataInputRandomDataOutput的组合)
    3. 假设提供了适当的SizedReaderSizedWriter(或DataAccess)。请注意,使用了“字节/元素联合大小”技术,与^{} and ^{} doc section^{}中给出的示例相同。您可以将LongListMarshaller基于该示例类
    4. 此强制转换已指定,请参见^{} javadocthe section about checksums in the doc。如果您有一个纯内存(而不是持久化的)历史记录映射,或者关闭了校验和,则可以忽略此调用

    单元素读取的实现类似

  2. # 2 楼答案

    回答额外问题:

    I've implemented a SizedReader+Writer. Do I need DataAccess or is SizedWriter fast enough for primitive arrays? I looked at the ByteArrayDataAccess but it's not clear how to port it for long arrays given that the internal HeapBytesStore is so specific to byte[]/ByteBuffers?

    使用DataAccess而不是SizedWriter可以在Map.put(key, value)上创建一个值较小的数据副本。然而,如果在您的用例中putOneValue()(如上面的例子)是主要的查询类型,那么它不会有太大的区别。如果Map.put(key, value)(和replace()等,即任何“全值写入”操作)很重要,那么仍然可以为LongList实现DataAccess。它看起来是这样的:

    class LongListDataAccess implements DataAccess<LongList>, Data<LongList>,
            StatefulCopyable<LongListDataAccess> {
        transient ByteStore cachedBytes;
        transient boolean cachedBytesInitialized;
        transient LongList list;
    
        @Override public Data<LongList> getData(LongList list) {
            this.list = list;
            this.cachedBytesInitialized = false;
            return this;
        }
    
        @Override public long size() {
            return ((long) list.size()) * Long.BYTES;
        }
    
        @Override public void writeTo(RandomDataOutput target, long targetOffset) {
            for (int i = 0; i < list.size(); i++) {
                target.writeLong(targetOffset + ((long) i) * Long.BYTES), list.get(i));
            }
        }
    
        ...
    }
    

    为了提高效率,方法size()writeTo()是关键。但是正确地实现所有其他方法(我在这里没有写)也很重要。非常仔细地阅读DataAccessDataStatefulCopyablejavadocs,也要非常注意教程中的Understanding ^{}^{} and ^{}Custom serialization checklist


    Does the read/write locking mediate across multiple process reading and writing on same machine or just within a single process?

    跨进程是安全的,请注意,接口名为InterProcessReadWriteUpdateLock


    When storing objects, with a variable size not known in advance, as values will that cause fragmentation off heap and in the persisted file?

    将一个键的值存储一次,之后不更改值的大小(也不删除键),不会导致外部碎片。更改值的大小或删除键可能会导致外部碎片^{}配置允许在外部和内部碎片之间进行交易。区块越大,外部碎片越少,但内部碎片越多。如果您的值明显大于页面大小(4KB),那么您可以设置非常大的块大小,并且仍然有页面大小限制的内部碎片,因为Chronicle Map能够利用Linux中的延迟页面分配功能