2012-08-24 90 views
6

我有一个List<Thing> things,其中Thing需要经常检索通过查找两个变量T1 f1T2 f2,这是值类型的组合。他们的方式我现在只是things.Where(t => t.Field1 == f1 && t.Field2 == f2)。但是,我经常做很多这样的查找,并且需要更有效的方法。词典支持重复的多维键?

幸运的是,things不需要删除或添加元素,所以我想到解析构建列表并添加到Dictionary<T1, Lookup<T2, Thing>>。但是,这感觉很混乱,特别是增加了解析。如果我需要查找更多的领域,它会变得非常多毛。三个字段看起来像Dictionary<T1, Dictionary<T2, Lookup<T3, Thing>>>

我的下一个想法是做一个Lookup<Tuple<T1,T2,T3,...>,Thing>。但在这种情况下,我不确定这些键是否会实际工作,因为Tuple是一个引用类型。

即使我做了一个Lookup<ValueType<T1,T2,T3,...>,Thing> things,查找语句将会像things[new ValueType<T1,T2,T3,...>(f1, f2, f3, ...)]这是非常丑陋的(我仍然不确定我是否可以信任这些键)。

是否有一个更优雅的解决方案,以保持散列表的性能优势,我可以简单地键入类似IEnumerable<Thing> found = things[f1, f2, f3, ...];

+1

你有没有考虑在内存数据库中使用类似SQLite的东西? – CodingGorilla

+0

'Thing'是否有识别标识(ID,PrimaryKey或其他)? –

+0

[C#多键通用字典](http://www.codeproject.com/Articles/32894/C-Multi-key-Generic-Dictionary) –

回答

3

Lookup<Tuple<T1,T2,T3,...>,Thing>会的工作,因为Tuple覆盖EqualsGetHashCode

为了使查找语法不那么难看,您可以使用支持类型推断的Tuple.Create。您的密码变为things[Tuple.Create(f1, f2, f3, ...)]。如果这仍然太难看,那么添加一个将各个值作为参数的帮助器方法是微不足道的。

我还会考虑为密钥创建我自己的不可变类(或值类型),因此您可以获得干净的字段名称而不是ItemX。您只需要始终覆盖EqualsGetHashCode

+0

也许我正在做些什么。这肯定看起来像简单,整洁的解决方案。我不确定你的意思是“所以你得到干净的字段名称而不是'ItemX'”?我该如何重写'Equals'和'GetHashCode'?我不知道这些实现应该如何。 –

+1

如果使用'Tuple'类,就会出现'ItemX'。这个想法是创建自己的类,它比'Item1','Item2'等具有更好的属性名称。 – Oliver

+0

元组生成大量哈希碰撞。这不是一个关键的好人选。 – Paparazzi

1

你有没有考虑过使用某种字段组合作为关键字的哈希表?我不知道你的数据集是否可行。由于密钥需要是唯一的。但是,由于你没有使用散列表进行添加或删除,所以在内存中查找的速度大概是你可以得到的速度。

+0

他明显做到了,元组是字段的组合,'Lookup'是一个散列表。 – CodesInChaos

1

如果我得到你的权利,你可以使用HashtableTuple,下面的例子:(如果重复键需要)

 // populate Hastable 
     var hash = new Hashtable();    
     var tuple = Tuple.Create("string", 1, 1.0); 
     hash.Add(tuple,tuple); 

     // search for item you want 
     var anotherTuple = Tuple.Create("string", 1, 1.0); 
     // result will be tuple declared above 
     var result = hash[anotherTuple]; 

更复杂的解决方案:

public class Thing 
{ 
    public int Value1 { get; set; } 

    public double Value2 { get; set; } 

    public string Value3 { get; set; } 

    // preferable to create own Equals and GetHashCode methods 
    public Tuple<int, double> GetKey() 
    { 
     // create key on fields you want 
     return Tuple.Create(Value1, Value2); 
    } 
} 

使用

var t1 = new Thing() {Value1 = 1, Value2 = 1.0, Value3 = "something"}; 
var t2 = new Thing() {Value1 = 1, Value2 = 2.0, Value3 = "something"}; 
var hash = new [] { t1, t2 }.ToLookup(item => item.GetKey()); 

var criteria = new Thing() { Value1 = 1, Value2 = 2.0, value3 = "bla-bla-bla" }; 
var r = hash[criteria.GetKey()]; // will give you t1 
+0

重复键失败,为什么使用非泛型散列表? – CodesInChaos

+0

我不认为这个集合应该包含相同的项目。 'Hastable' - 用于代码*简化*。 – user854301

+0

不幸的是,我的需求需要重复键 –

2

您可以创建多个查找,然后将它们相交以完成您的搜索CHES。这是一个有点过于简单的例子,但它应该说明的想法:

class Test { 
    public string A { get; set; } 
    public string B { get; set; } 
    public string C { get; set; } 
} 

var list = new List<Test> { 
    new Test {A = "quick", B = "brown", C = "fox"} 
, new Test {A = "jumps", B = "over", C = "the"} 
, new Test {A = "lazy", B = "dog", C = "quick"} 
, new Test {A = "brown", B = "fox", C = "jumps"} 
, new Test {A = "over", B = "the", C = "lazy"} 
, new Test {A = "dog", B = "quick", C = "brown"} 
, new Test {A = "fox", B = "jumps", C = "over"} 
, new Test {A = "the", B = "lazy", C = "dog"} 
, new Test {A = "fox", B = "brown", C = "quick"} 
, new Test {A = "the", B = "over", C = "jumps"} 
, new Test {A = "quick", B = "dog", C = "lazy"} 
, new Test {A = "jums", B = "fox", C = "brown"} 
, new Test {A = "lazy", B = "the", C = "over"} 
, new Test {A = "brown", B = "quick", C = "dog"} 
, new Test {A = "over", B = "jumps", C = "fox"} 
, new Test {A = "dog", B = "lazy", C = "the"} 
}; 
var byA = list.ToLookup(v => v.A); 
var byB = list.ToLookup(v => v.B); 
var byC = list.ToLookup(v => v.C); 
var all = byA["quick"].Intersect(byB["dog"]); 
foreach (var test in all) { 
    Console.WriteLine("{0} {1} {2}", test.A, test.B, test.C); 
} 
all = byA["fox"].Intersect(byC["over"]); 
foreach (var test in all) { 
    Console.WriteLine("{0} {1} {2}", test.A, test.B, test.C); 
} 

这将打印

quick dog lazy 
fox jumps over 
+0

如果您搜索的最稀有的词语足够稀少,速度会更快。如果不是,可能会变慢。 – CodesInChaos

+0

@CodesInChaos这是真的,如果单词分布不好,你不会得到太多的加速。尽管我想你仍然会击败帖子顶部描述的“全面扫描”方法。 – dasblinkenlight

+0

对此的一个轻微变体是使用查找最稀有的单词,然后用'Where'从那里过滤。可能会稍微快一点,并占用较少的内存。 – CodesInChaos

0

Linq字典或字典可能是最漂亮的,你会得到。但它可能更像是你如何组织数据的问题。

E.G.这永远不会是人们访问数据的方式漂亮:

people["FirstName"]["LastName"] 

它通常更好,所以试图想出了一个简单的键。