Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在大列表中查找具有相同属性的对象-性能缓慢_C#_List - Fatal编程技术网

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};