我们有一个Hashtable(特别是C#Dictionary
类),它拥有数千/数百万(Key,Value)对,用于近O(1)搜索命中/未命中。
我们希望能够将此数据结构刷新到磁盘(序列化)并稍后再次加载(反序列化),以便保留Dictionary的内部哈希表。
我们现在做的是:
List<KVEntity>
。 (KVEntity
是可序列化的。我们使用Avro进行序列化 - 如果需要可以删除Avro)KVEntity
字典。 重新生成 字典/哈希表内部状态。myKVDict.Values.SelectMany(x => x)
转换为新的List<KVEntity>
)List<KVEntity>
)序列化到磁盘以保存原始数据请注意,在我们的保存/恢复期间,我们会丢失内部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;}
答案 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代码,测试,基准测试和写作。
为了完整性,我将在这里添加我自己的实现。