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

时间:2015-12-11 11:39:50

标签: c# .net linq list foreach

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

我有两个不同类的2个列表。

头等舱:

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

这个解决方案是我的意图,但非常难看,我有2个foreach循环,可能很慢(列表可以包含超过10 000个元素)。在示例类中,我只编写了3个属性来显示它是如何工作的,但最初它每个都有~20个属性,而且只有2个属性相同。我可以更快更有效地完成这项工作吗?有些LinQ也许吧?对不起,我无法显示更多代码。我希望你能理解我的要求。 我只需要从Class2List获取一个属性,并且仅当列表中的一个参数相同时才将其放在Class1List上。

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

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

这应该更快但仍然看起来很丑陋

2 个答案:

答案 0 :(得分:3)

所以我在这里有三个选择:

使用LINQ

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

这使得我的机器4.58s在每个列表中有20000个元素。虽然代码看起来(对我来说)好一点,但这与你的代码有效。

使用字典

使用字典通过哈希访问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.00878s。

并行

如果您的数据真的很大,您可以考虑使用Parallel.ForEach

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.0197s)。

答案 1 :(得分:1)

弄清楚这是一件非常有趣的事情,但我觉得这样的事情可能有用:

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)