在大型列表中查找具有相同属性的对象—性能缓慢

本文关键字:属性 对象 缓慢 性能 列表 大型 查找 | 更新日期: 2023-09-27 18:15:58

我有一个很大的对象List<MyClass>,大约有600000个。MyClass大约有10个属性,比如说property1property2等。直到property10

从这个列表中,我想得到一个List<MyClass>的列表,其中一些属性的对象具有相同的值。

这意味着例如property2property4property8property10相同的对象。

最好的方法是什么?目前,我在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();
}
相关文章: