用其他属性从第二个列表填充列表中数据的更快替代方案

本文关键字:列表 方案 数据 属性 其他 第二个 填充 | 更新日期: 2023-09-27 18:26:22

我在SO上搜索了一个解决方案,但找不到,所以我提出了这个新问题。

我有两个不同类别的清单。

头等舱:

public class Class1
{
    public int C1Property1 {get;set;}
    public int C1Property2 {get;set;}
    public int C1Property3 {get;set;}
}

第二类:

public class Class2
{
    public int C2Property1 {get;set;}
    public int C2Property2 {get;set;}
    public int C2Property3 {get;set;}
}

我有两个类的列表:

List<Class1> Class1List = new List<Class1>();
List<Class2> Class2List = new List<Class2>();

现在我遇到了困难:两个类中的两个属性具有相同的值,但名称不同:即C1Property1 = C2Property1C1Property2 = C2Property2。列表Class1List的属性C1Property1为空,我需要使用Class2List中的属性来填充它。我通过使用以下代码来做到这一点:

foreach(var element1 in Class1List)
{
     foreach(var element2 in Class2List)
     {
         if(element2.C2Property2 == element1.C1Property2)
         {
             element1.C1Property1 = element2.C2Property1;
         }
     }
}

这个解决方案按照我的意图工作,但非常难看,我有两个foreach循环,速度可能很慢(列表可能包含超过10000个元素)。在示例类中,我只写了3个属性来展示它的工作原理,但最初它每个都有大约20个属性,其中只有2个是相同的。我能更快、更高效地做到这一点吗?也许是林克?对不起,我无法显示更多代码。我希望你能理解我的要求。我只需要从Class2List中获取一个属性,并仅当列表中的一个参数相同时将其放置在Class1List上。

在我的第二次尝试中,我使用了类似的东西:

foreach (var element1 in Class1List)
{
    foreach (var element2 in Class2List.Where(element2 => element2.C2Property2 == element1.C1Property2 ))
    {
        element2.C2Property2 = element1.C1Property2;
        break;
    }
}

这应该更快,但看起来仍然很难看

用其他属性从第二个列表填充列表中数据的更快替代方案

所以这里有三个选项:

使用LINQ

Class1List.ForEach(element1 =>
{
    Class2 element2 = Class2List.FirstOrDefault(e2 => e2.C2Property2 == element1.C1Property2);
    if (element2 != null) element1.C1Property1 = element2.C2Property1;
});

对于每个列表中的20000个元素,我的机器花费了4.58秒。虽然代码看起来(对我来说)稍微好一点,但这实际上与您的代码相同。

使用字典

使用字典通过散列访问Class2元素是非常有效的:

Dictionary<int, Class2> dictionary =                Class2List.GroupBy(e2 => e2.C2Property2, e2 => e2).Select(elements => elements.First()).ToDictionary(e2 => e2.C2Property2, e2 => e2);
Class1List.ForEach(element1 =>
{
    if (dictionary.ContainsKey(element1.C1Property2))
        element1.C1Property1 = dictionary[element1.C1Property2].C2Property1;
});

对于每个列表中的20000个元素,我的机器花费了0.00878秒。

平行

如果你的数据真的非常大,你可以考虑使用Parallel.For Each

Dictionary<int, Class2> dictionary =
            Class2List.GroupBy(e2 => e2.C2Property2, e2 => e2).Select(elements => elements.First()).ToDictionary(e2 => e2.C2Property2, e2 => e2);
Parallel.ForEach(Class1List, element1 =>
{
    if (dictionary.ContainsKey(element1.C1Property2))
        element1.C1Property1 = dictionary[element1.C1Property2].C2Property1;
});

但由于每个列表中只有20000个元素,我的机器比非并行版本花费了更长的时间(0.0197秒)。

这是一件非常有趣的事情,但我认为这样的事情可能会奏效:

Class1List.ForEach(c1 =>
    c1.C1Property1 = Class2List.Where(c2 => c2.C2Property2 == c1.C1Property2)
                               .Select(r => r.C2Property1)
                               .FirstOrDefault());

这是一个测试类:

using System;
using System.Collections.Generic;
using System.Linq;
namespace SO_Test
{
    public class ObjectA
    {
        public int Property1 { get; set; }
        public int? Property2 { get; set; }
        public override string ToString()
        {
            return String.Format("({0}, {1})", Property1, Property2);
        }
    }
    public class ObjectB
    {
        public int Property1 { get; set; }
        public int? Property2 { get; set; }
        public override string ToString()
        {
            return String.Format("({0}, {1})", Property1, Property2);
        }
    }
    class Program
    {
        static void Main()
        {
            var listA = new List<ObjectA>
            { 
                new ObjectA { Property1 = 5,  Property2 = null }, 
                new ObjectA { Property1 = 16, Property2 = null }, 
                new ObjectA { Property1 = 9,  Property2 = null }, 
                new ObjectA { Property1 = 38, Property2 = null } 
            };
            var listB = new List<ObjectB>
            { 
                new ObjectB { Property1 = 5, Property2 = 1 }, 
                new ObjectB { Property1 = 9, Property2 = 2 }, 
                new ObjectB { Property1 = 16, Property2 = 3 } 
            };
            Console.WriteLine("BEFORE");
            Console.WriteLine("ListA: {0}", String.Join(", ", listA));
            Console.WriteLine("ListB: {0}", String.Join(", ", listB));
            listA.ForEach(a =>
                a.Property2 = listB.Where(b => b.Property1 == a.Property1)
                                   .Select(r => r.Property2)
                                   .FirstOrDefault());
            Console.WriteLine("AFTER");
            Console.WriteLine("ListA: {0}", String.Join(", ", listA));
            Console.WriteLine("ListB: {0}", String.Join(", ", listB));
            Console.ReadLine();
        }
    }
}

输出:

BEFORE
ListA: (5, ), (16, ), (9, ), (38, )
ListB: (5, 1), (9, 2), (16, 3)
AFTER
ListA: (5, 1), (16, 3), (9, 2), (38, )
ListB: (5, 1), (9, 2), (16, 3)