C# 在大列表中查找具有相同属性的对象-性能缓慢
我有一个庞大的C# 在大列表中查找具有相同属性的对象-性能缓慢,c#,list,C#,List,我有一个庞大的对象列表,大约60万个。 MyClass大约有10个属性,比如说property1,property2,等等。。直到属性10 从这个列表中,我想得到一个list列表,其中一些属性的对象具有相同的值 这意味着,例如,property2、property4、property8和property10中的对象是相同的 最好的方法是什么?目前,我在我的列表上做了一个循环,在该循环中,我通过List.FindAll(),伪代码获得了所有类似的对象: forach(var item in myC
对象列表,大约60万个。
MyClass
大约有10个属性,比如说property1
,property2
,等等。。直到属性10
从这个列表中,我想得到一个list
列表,其中一些属性的对象具有相同的值
这意味着,例如,property2
、property4
、property8
和property10
中的对象是相同的
最好的方法是什么?目前,我在我的列表上做了一个循环,在该循环中,我通过List.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()方法非常慢
有更有效的算法吗?首先添加一个方法,GetUniqueKey
,该方法在类中返回唯一的键(散列)
然后,使用分组查找具有相似键的项:
List<List<Item>> = items
.GroupBy(item => item.GetUniqueKey())
.Select(g => g.ToList())
.ToList();
或(更优化)
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();
}
类项目
{
公共int Prop1{get;set;}
公共int Prop2{get;set;}
公共字符串GetUniqueKey()
{
返回Prop1.ToString()+“-”+Prop2.ToString();
}
}
公共工作
{
随机rnd=新随机();
列表项=新列表();
对于(int i=0;i<600000;i++)
{
添加(新项目{Prop1=rnd.Next(1,10)});
}
对于(int i=0;i<600000;i++)
{
项目[i].Prop2=rnd.Next(1,13);
}
列表=项目
.GroupBy(item=>item.GetUniqueKey())
.Select(g=>g.ToList())
.ToList();
}
您可以使用分组方式
非常有效地解决此问题:
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
和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)));
}
}
}
}
使用PLINQ进行可能的优化
正如@BertPersyn在下面提到的,您也许可以使用PLINQ来加快速度
要做到这一点,只需使用以下命令生成grouped
(注意添加了.aspallel()
):
要确定这是否确实加快了速度,您必须执行一些计时。使用Where()
而不是FindAll
这个大列表来自哪里?如果它来自SQL数据库,那么在查询中添加一个好的WHERE子句可能会更有效。此外,如果列表是按顺序排列的,则可能会有所帮助—然后您可以重写代码以利用这种顺序。@geo:我想我已经用类似的方法做到了results@S.L.巴斯:这个列表是从一个文件反序列化而来的。如果可能的话,我决定不在我的项目中使用DB。@geo不确定为什么要将FindAll替换为该程序中的Where。事实上,这会让事情变得更糟,因为类似的对象必须迭代两次。也就是说,对于这个程序来说,GroupBy才是真正需要的。如果CLR允许,可以尝试使用并行类。@BertPersyn好主意,我会把它添加到答案中。谢谢!稍后我将试用它,并让您知道它的性能。使用AsParallel()从10多小时下降到6秒-非常好,非常感谢!同样的,我稍后会尝试一下,并告诉你结果。目前有两个答案,期待测试!同样在这里-从10多小时下降到8秒-也很好,非常感谢。然而,马克·马修斯会回答这个问题,因为它更容易阅读,也不需要在我的类中添加额外的方法。不过,非常感谢!
foreach (var group in grouped)
{
foreach (var item in group)
{
// Do something with item
}
}
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
var grouped =
from item in myClassList.AsParallel()
group item
by new {item.Property2, item.Property4, item.Property8, item.Property10};