6

我有一个List<Thing> things,其中Thing需要通过查找两个变量T1 f1和的组合来经常检索多个 s T2 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, ...];

4

5 回答 5

3

Lookup<Tuple<T1,T2,T3,...>,Thing>将起作用,因为Tuple覆盖EqualsGetHashCode.

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

我还考虑为键创建自己的不可变类(或值类型),这样你就可以得到干净的字段名称而不是ItemX. 您只需要覆盖EqualsGetHashCode始终如一。

于 2012-08-24T17:11:18.547 回答
2

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

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
于 2012-08-24T17:09:03.070 回答
1

您是否考虑过使用带有某种字段组合的哈希表作为键?我对您的数据集知之甚少,无法说明这是否可行。因为密钥需要是唯一的。但是,由于您没有使用哈希表进行添加或删除,因此在内存中查找的速度大约是您可以获得的最快速度。

于 2012-08-24T17:03:10.317 回答
1

如果我猜对了,您可以使用Hashtablewith Tuple,例如:

        // 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
于 2012-08-24T17:03:22.733 回答
0

Linq Where 或 Dictionary of Dictionaries 可能是你会得到的最漂亮的。但这可能更多是您如何组织数据的问题。

EG 这永远不会是访问人员数据的好方法:

people["FirstName"]["LastName"] 

这通常会更好,所以尝试想出一个更简单的密钥。

于 2012-08-24T17:12:40.663 回答