给定一个通用List我需要某种索引(在数据库意义上),这将允许我快速检索。这个索引的键不是唯一的,所以我不能使用字典。这就是我的想法:给定一个类Foo {P1,P2,P3}可能有这样的数据
{ "aaa", 111, "yes" }
{ "aaa", 112, "no" }
{ "bbb", 111, "no" }
{ "bbb", 220, "yes" }
{ "bbb", 220, "no" }
{ "ccc", 300, "yes" }
我需要快速访问P1为“bbb”(第3,第4和第5)的所有记录或P2为111(第1和第3)的所有记录。我可以使用排序列表,但如果我需要多种排序/索引方法,我最终会得到重复的列表。
.NET框架中是否内置了某些东西,或者可能会有类似这样的操作系统库?感谢。
P.S。我提到“排序列表”的想法是排序列表将更快地返回/查找项目。我不需要列表必须排序;我只是在寻找快速检索/发现。
答案 0 :(得分:13)
永远不要忘记这个原则:正确,明确,简洁,快速。以该顺序。所以,首先编写天真的实现代码:
static IEnumerable<T> GetByIndex<T>(
List<T> list,
Func<T, TIndex> func,
TIndex key
) {
return list.Where(x => func(x) == key);
}
用法:
List<Test> tests = new List<Test>() {
new Test { Name = "aaa", Value = 111, Valid = Valid.Yes },
new Test { Name = "aaa", Value = 111, Valid = Valid.Yes },
new Test { Name = "bbb", Value = 112, Valid = Valid.No },
new Test { Name = "bbb", Value = 111, Valid = Valid.No },
new Test { Name = "bbb", Value = 220, Valid = Valid.No },
new Test { Name = "ccc", Value = 220, Valid = Valid.Yes }
};
IEnumerable<Test> lookup = GetByIndex(tests, x => x.Name, "bbb");
以上是正确,清晰和简洁的。几乎可以肯定它足够快到你的目的。
因此,只要加快速度,你必须先测量:
然后,当且仅当这个不够快时,你应该尝试优化。实现允许您索引各种属性的IndexedList<T> : ICollection<T>
并不会太难。
这是一个可以帮助您入门的天真实现:
class IndexedList<T> : IEnumerable<T> {
List<T> _list;
Dictionary<string, Dictionary<object, List<T>>> _dictionary;
Dictionary<string, Func<T, object>> _propertyDictionary;
public IndexedList(IEnumerable<string> propertyNames) : this(propertyNames, new List<T>()) { }
public IndexedList(IEnumerable<string> propertyNames, IEnumerable<T> source) {
_list = new List<T>();
_dictionary = new Dictionary<string, Dictionary<object, List<T>>>();
_propertyDictionary = BuildPropertyDictionary(propertyNames);
foreach (var item in source) {
Add(item);
}
}
static Dictionary<string, Func<T, object>> BuildPropertyDictionary(IEnumerable<string> keys) {
var propertyDictionary = new Dictionary<string,Func<T,object>>();
foreach (string key in keys) {
ParameterExpression parameter = Expression.Parameter(typeof(T), "parameter");
Expression property = Expression.Property(parameter, key);
Expression converted = Expression.Convert(property, typeof(object));
Func<T, object> func = Expression.Lambda<Func<T, object>>(converted, parameter).Compile();
propertyDictionary.Add(key, func);
}
return propertyDictionary;
}
public void Add(T item) {
_list.Add(item);
foreach (var kvp in _propertyDictionary) {
object key = kvp.Value(item);
Dictionary<object, List<T>> propertyIndex;
if (!_dictionary.TryGetValue(kvp.Key, out propertyIndex)) {
propertyIndex = new Dictionary<object, List<T>>();
_dictionary.Add(kvp.Key, propertyIndex);
}
List<T> list;
if (!propertyIndex.TryGetValue(key, out list)) {
list = new List<T>();
propertyIndex.Add(key, list);
}
propertyIndex[key].Add(item);
}
}
public IEnumerable<T> GetByIndex<TIndex>(string propertyName, TIndex index) {
return _dictionary[propertyName][index];
}
public IEnumerator<T> GetEnumerator() {
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
用法:
List<Test> tests = new List<Test>() {
new Test { Name = "aaa", Value = 111, Valid = Valid.Yes },
new Test { Name = "aaa", Value = 111, Valid = Valid.Yes },
new Test { Name = "bbb", Value = 112, Valid = Valid.No },
new Test { Name = "bbb", Value = 111, Valid = Valid.No },
new Test { Name = "bbb", Value = 220, Valid = Valid.No },
new Test { Name = "ccc", Value = 220, Valid = Valid.Yes }
};
// build an IndexedList<Text> indexed by Name and Value
IndexedList<Test> indexed = new IndexedList<Test>(new List<string>() { "Name", "Value" }, tests);
// lookup where Name == "bbb"
foreach (var result in indexed.GetByIndex("Name", "bbb")) {
Console.WriteLine(result.Value);
}
但是请注意,除非天真的实现还不够快,否则你不这样做的原因是你刚刚添加到系统中的额外复杂性。您刚刚添加了新代码来维护,新代码可以测试,如果现实数据速度不快或者不是应用程序的瓶颈,则可能无法获得任何代码。
答案 1 :(得分:12)
(编辑详细阐述基于收集的策略)
.NET中没有内部结构可供查找使用各种索引。以下是两个好策略:
选项1 :LINQ ,灵活性和简单性 为了简单和许多其他集成选项,创建一个自定义类型的List(或其他实现IEnumerable的东西)并使用LINQ进行按需查找。请注意,如果方便,您可以使用匿名类型。您还可以将数据保存在XML结构中,并且仍然可以完成所有这些操作。您可能能够获取数据,进行查找,并以少量清晰的代码操作结果。在.Net 4.0中,您可以使用 parallel Ling(PLINQ)轻松地利用多核处理这一过程。
List<foo> bigFooList = new List<foo>
{
new Foo {"aaa", 111, "yes"},
new Foo {"aaa", 112, "no"},
new Foo {"bbb", 111, "no"},
new Foo {"bbb", 220, "yes"},
new Foo {"bbb", 220, "no"},
new Foo {"ccc", 300, "yes"}
};
var smallFooList = From f In bigFooList Where f.P2 = 220 Select f;
选项2 :多个集合,用于索引查找电源。
如果您在大型集上执行大量查找并需要电源,则可以使用多个集合来实现更快的查找。棘手的部分是您要求可以复制索引值。以下是一些策略:
Lookup<string, foo> LookupP1 = (Lookup<string, foo>) fooList.ToLookup(f => f.P1, f => p)
Dictionary<T, List<foo>>
,其中T是该值的类型。因此,对于您的示例,我们将创建:var FoosByP1 = new Dictionary<String,List<foo>>
var FoosByP2 = new Dictionary<Int32,List<foo>>
等
然后添加到FoosByP1,键入每个唯一的P1值,List包含P1具有该值的所有foo项。 (例如,由“aaa”键入,包含P1为“aaa”的所有foo对象的List。)对每个Foo字段重复。根据您的数据,FoosByP1You将包含3个List对象,分别包含2,3和1个foo项。使用此方案,您可以非常快速地检索。 (字典基本上是一个哈希表)
主要问题是您的数据将在每个词典中重复,这可能是也可能不是问题。如果Foo有 20 个字段且你有很多foo项,你可以通过一个带有数字键和所有foo项的中心字典来节省内存,而单个索引字典将改为{{1} },其中整数将是中央字典中Foo项的索引。这样可以节省内存并且速度非常快
无论你是否有中心词典,建立你的Dictonaries都需要一些cpu周期,但是一旦你拥有它们,你将会处于良好的状态。并使用Linq来构建你的词典!答案 2 :(得分:2)
一种方法是使用嵌入式关系数据库和SQLite(这里有一个ADO.NET绑定:http://sqlite.phxsoftware.com/)
大多数数据结构都不符合您的要求,除非您愿意每次重新排序列表/任何内容,因为您需要不同的顺序。
答案 3 :(得分:2)
我从未真正有机会使用它,但您可以尝试i4o。它应该为内存中的对象提供索引以供Linq使用。您可以使用任一属性或构建索引器来指定类的索引,然后创建IndexableCollection。
此时,您只需使用Linq查询集合,并且索引在幕后工作以优化数据的访问模式。
答案 4 :(得分:0)
您可能需要考虑像Lucene.Net这样的索引和搜索库。我不知道这可能是一个比你想要的更复杂的解决方案,但它肯定会满足你的性能需求。
答案 5 :(得分:0)
我知道你说你不能使用字典,但是下面会有用吗?
对于您的示例数据集:
{ "aaa", 111, "yes" }
{ "aaa", 112, "no" }
{ "bbb", 111, "no" }
{ "bbb", 220, "yes" }
{ "bbb", 220, "no" }
{ "ccc", 300, "yes" }
您可以使用以下内容:
var p1Lookup = new Dictionary<string,int []>();
p1Lookup.Add( "aaa", new int [] {0, 1} );
p1Lookup.Add( "bbb", new int [] {2, 3, 4} );
p1Lookup.Add( "ccc", new int [] {5} );
var p2Lookup = new Dictionary<int,int []>();
p1Lookup.Add( 111, new int [] {0, 2} );
p1Lookup.Add( 112, new int [] {1} );
p1Lookup.Add( 220, new int [] {3, 4} );
p1Lookup.Add( 300, new int [] {5} );
var p3Lookup = new Dictionary<int,int []>();
p1Lookup.Add( "yes", new int [] {0, 3, 5} );
p1Lookup.Add( "no", new int [] {1, 2, 4} );
根据使用情况,您只需构建一次查找词典
答案 6 :(得分:0)
如果你只需要迭代列表一次,但是多次搜索它,并且很少改变它(因为DB索引是最好的)。一旦建成,字典就会非常快。我的方法不会创建重复项。
var indexDict = new Dictionary<string, List<int>>();
for(int ct = 0; ct < pList.length; ct++)
{
var item = pList[ct];
if (!indexDict.ContainsKey(item.toIndexBy))
{
indexDict.Add(item.toIndexBy, new List<int> { ct };
}
else
{
indexDict[item.toIndexBy].add(ct);
}
}
现在您可以快速查找索引。
因此,如果您想要&#34; bbb&#34;的索引,您可以这样做:
int bbbIndexes = indexDict["bbb"];
答案 7 :(得分:-2)
为什么不使用HashSet来存储Foo对象的不同实例(这将是唯一的),然后使用LINQ查询来检索与给定条件匹配的实例?
类似的东西:
var hash = new HashSet<Foo>
{
new Foo { P1 = "aaa", P2 = 111, P3 = "yes"},
new Foo { P1 = "aaa", P2 = 112, P3 = "no"},
new Foo { P1 = "bbb", P2 = 111, P3 = "no"},
new Foo { P1 = "bbb", P2 = 220, P3 = "yes"},
new Foo { P1 = "bbb", P2 = 220, P3 = "no"},
new Foo { P1 = "ccc", P2 = 300, P3 = "yes"},
};
var results = from match in hash
where match.P1 == "aaa"
select match;