将字典序列化到磁盘?

时间:2014-01-04 01:29:54

标签: c# serialization dictionary

我们有一个Hashtable(特别是C#Dictionary类),它拥有数千/数百万(Key,Value)对,用于近O(1)搜索命中/未命中。

我们希望能够将此数据结构刷新到磁盘(序列化)并稍后再次加载(反序列化),以便保留Dictionary的内部哈希表。

我们现在做的是:

  1. 从磁盘加载=&gt; List<KVEntity>。 (KVEntity是可序列化的。我们使用Avro进行序列化 - 如果需要可以删除Avro)
  2. 从array =&gt;中读取每个KVEntity字典。 重新生成 字典/哈希表内部状态。
  3. &LT;系统运行,字典可以增长/缩小/改变值等&gt;
  4. 保存时,请从字典中读取数组(通过myKVDict.Values.SelectMany(x => x)转换为新的List<KVEntity>
  5. 我们将数组(List<KVEntity>)序列化到磁盘以保存原始数据
  6. 请注意,在我们的保存/恢复期间,我们会丢失内部tashtable /字典状态,并且每次都必须重建它。

    我们想直接序列化到/来自Dictionary(包括它的内部“实时”状态),而不是仅仅为磁盘i / o使用中间数组。我们怎么做?

    一些伪代码:

    // The actual "node" that has information. Both myKey and myValue have actual data work storing
    public class KVEntity
    {
        public string myKey {get;set;}
        public DataClass myValue {get;set;}
    }
    
    // unit of disk IO/serialization
    public List<KVEntity> myKVList {get;set;} 
    
    // unit of run time processing. The string key is KVEntity.myKey
    public Dictionary<string,KVEntity> myKVDict {get;set;} 
    

2 个答案:

答案 0 :(得分:7)

存储Dictionary实例的内部状态将是不好的做法 - OOP的一个关键原则是封装:内部实现细节是故意隐藏的消费者。

此外,Dictionary使用的映射算法可能会在不同版本的.NET Framework中发生变化,特别是考虑到CIL程序集设计为向前兼容(即,针对.NET 2.0编写的程序通常可以正常工作反对.NET 4.5)。

最后,序列化字典的内部状态并没有真正的性能提升。使用定义明确的文件格式要好得多,重点放在可维护性而不是速度上。此外,如果字典包含“几千个”条目,那么我应该在15ms内从磁盘加载(假设你有一个有效的磁盘格式)。最后,针对RAM优化的数据结构不一定能够在顺序读/写更好的磁盘上正常工作。

你的帖子非常坚持使用字典的内部状态,但你现有的方法似乎很好(albiet,它可以做一些优化)。如果您透露了更多详细信息,我们可以帮助您加快速度。

的优化

我在现有实现中看到的主要问题是转换到数组和列表的转换,这是不必要的,因为Dictionary可以直接枚举。

我会做这样的事情:

Dictionary<String,TFoo> dict = ... // where TFoo : new() && implements a arbitrary Serialize(BinaryWriter) and Deserialize(BinaryReader) methods

using(FileStream fs = File.OpenWrite("filename.dat"))
using(BinaryWriter wtr = new BinaryWriter(fs, Encoding.UTF8)) {

    wtr.Write( dict.Count );

    foreach(String key in dict.Keys) {

        wtr.Write( key );
        wtr.Write('\0');
        dict[key].Serialize( wtr );
        wtr.Write('\0'); // assuming NULL characters can work as record delimiters for safety.
    }
}

假设您的TFoo Serialize方法速度很快,我认为您的速度不会超过此方法。

实现反序列化器是读者的练习,但应该是微不足道的。请注意我是如何将字典的大小存储到文件中的,因此返回的字典可以在创建时设置正确的大小,从而避免了@spender在评论中描述的重新平衡问题。

答案 1 :(得分:0)

因此,我们将坚持使用我们现有的策略,因为戴的推理和我们有C#和Java兼容性来维护(这意味着C#字典的额外树状态位将被丢弃在Java端,无论如何现在只加载节点数据。)

对于后来仍然对此感兴趣的读者,我发现了一个非常好的response here,它有点回答了提出的问题。一个关键的区别是这个答案适用于B+ Trees,而不是Dictionaries,尽管在实际应用中这两个数据结构的性能非常相似。 B + Tree性能比常规树更接近字典(如二进制,红黑,AVL等)。具体来说,字典提供接近O(1)的性能(但没有“从范围中选择”能力),而B +树有O(logb(X)),其中b = base通常很大,这使得它们与常规树相比非常高效。 = 2。我在这里复制粘贴它是为了完整性,但 所有功劳都归功于csharptest.net ,用于B + Tree代码,测试,基准测试和写作。

  

为了完整性,我将在这里添加我自己的实现。