在大型列表中查找具有相同属性的对象—性能缓慢
本文关键字:属性 对象 缓慢 性能 列表 大型 查找 | 更新日期: 2023-09-27 18:15:58
我有一个很大的对象List<MyClass>
,大约有600000个。MyClass
大约有10个属性,比如说property1
、property2
等。直到property10
。
从这个列表中,我想得到一个List<MyClass>
的列表,其中一些属性的对象具有相同的值。
这意味着例如property2
、property4
、property8
和property10
相同的对象。
最好的方法是什么?目前,我在List<MyClass>
上进行循环,在该循环中,我通过List<MyClass>.FindAll()
获得所有类似的对象,伪代码:
forach(var item in myClassList)
{
if(!found.Contains(item))
{
var similarObjects = myClassList.FindAll(x => x.property2 == item.property2 && x.property4 == item.property4 && x.property8 == item.property8 && x.property10 == item.property10);
//adding the objects to the "already found" list
foreach(var foundItem in similarOjbects)
{
found.Add(foundItem);
}
if(similarObjects.Count > 1)
{
similarObjectsList.Add(similarObjects);
}
}
}
但它需要很长时间,List.FindAll()
方法非常慢。
有没有更有效的算法可以做到这一点?
您可以使用group by
非常有效地解决此问题:
var grouped =
from item in myClassList
group item
by new {item.Property2, item.Property4, item.Property8, item.Property10};
这将为您提供一个组序列,其中每个组包含指定属性具有相同值的所有对象。
例如,要迭代得到的组序列中每组中的每个项目,可以执行以下操作:
foreach (var group in grouped)
{
foreach (var item in group)
{
// Do something with item
}
}
注意,这假设每个属性的类型实现IEquatable<T>
和GetHashCode()
。
这里有一个可编译的例子:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Data
{
public string Name { get; set; }
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
public int Property4 { get; set; }
public int Property5 { get; set; }
public int Property6 { get; set; }
public int Property7 { get; set; }
public int Property8 { get; set; }
public int Property9 { get; set; }
public int Property10 { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Data> myClassList = new List<Data>
{
new Data {Name = "1A", Property2 = 1, Property4 = 1, Property8 = 1, Property10 = 1},
new Data {Name = "1B", Property2 = 1, Property4 = 1, Property8 = 1, Property10 = 1},
new Data {Name = "1C", Property2 = 1, Property4 = 1, Property8 = 1, Property10 = 1},
new Data {Name = "2A", Property2 = 2, Property4 = 2, Property8 = 2, Property10 = 2},
new Data {Name = "2B", Property2 = 2, Property4 = 2, Property8 = 2, Property10 = 2},
new Data {Name = "2C", Property2 = 2, Property4 = 2, Property8 = 2, Property10 = 2},
new Data {Name = "3A", Property2 = 3, Property4 = 3, Property8 = 3, Property10 = 3},
new Data {Name = "3B", Property2 = 3, Property4 = 3, Property8 = 3, Property10 = 3},
new Data {Name = "3C", Property2 = 3, Property4 = 3, Property8 = 3, Property10 = 3},
};
var grouped =
from item in myClassList
group item
by new {item.Property2, item.Property4, item.Property8, item.Property10};
foreach (var group in grouped)
{
Console.WriteLine(string.Join(", ", group.Select(item => item.Name)));
}
}
}
}
上面的例子输出:
1A, 1B, 1C
2A, 2B, 2C
3A, 3B, 3C
使用PLINQ的可能优化
正如@BertPersyn在下面提到的,您也许可以使用PLINQ来加快速度。
要做到这一点,只需使用以下内容生成grouped
(注意添加了.AsParallel()
(:
var grouped =
from item in myClassList.AsParallel()
group item
by new {item.Property2, item.Property4, item.Property8, item.Property10};
为了确定这是否真的加快了速度,你必须进行一些计时。
首先添加一个方法GetUniqueKey
,该方法在类中返回一个唯一的密钥(hash(。
然后,使用分组来查找具有类似密钥的项目:
List<List<Item>> = items
.GroupBy(item => item.GetUniqueKey())
.Select(g => g.ToList())
.ToList();
GetUniqueKey方法应根据所需的属性类型进行实现和优化。例如,如果Property1和Property2是整数,则可以使用以下方法:
public string GetUniqueKey()
{
return Prop1.ToString() + "-" + Prop2.ToString();
}
OR(更优化(
public object GetUniqueKey()
{
return new { P1 = Prop1, P2 = Prop2 };
}
GetUniqueKey
示例方法本身可能没有优化,您可能会找到另一个优化的实现。
完整示例:
class Item
{
public int Prop1 {get; set;}
public int Prop2 {get; set;}
public string GetUniqueKey()
{
return Prop1.ToString() + "-" + Prop2.ToString();
}
}
public void DoWork()
{
Random rnd = new Random();
List<Item> items = new List<Item>();
for(int i = 0; i < 600000; i++)
{
items.Add(new Item { Prop1 = rnd.Next(1, 10) });
}
for(int i = 0; i < 600000; i++)
{
items[i].Prop2 = rnd.Next(1, 13);
}
List<List<Item>> = items
.GroupBy(item => item.GetUniqueKey())
.Select(g => g.ToList())
.ToList();
}