2012-08-24 93 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"] 

它通常更好,所以試圖想出了一個簡單的鍵。