【问题标题】:Fastest way to get matching items from two list c#从两个列表c#中获取匹配项的最快方法
【发布时间】:2017-07-07 10:35:23
【问题描述】:

我有两个列表

列表1 只有两个属性。 不能使用字典,因为可能有重复的键。 Property1 和 Property2 的组合是独一无二的。

public class List1
{
    public string Property1 { get; internal set; }
   public string Property2 { get; internal set; }
}

public class List2
{
    public string Property1 { get; internal set; }
    public string Property2 { get; internal set; }
    public string Property3 { get; internal set; }
}

List<List1> mylist1 = new List<List1>() {
    new List1() {Property1="664",Property2="Ford" },
    new List1() {Property1="665",Property2="Ford" },
    new List1() {Property1="664",Property2="Toyota" },
};

List<List2> mylist2 = new List<List2>() {
    new List2() {Property1="664",Property2="Ford" ,Property3="USA"},
    new List2() {Property1="665",Property2="Ford" ,Property3="USA"},
    new List2() {Property1="664",Property2="Toyota" ,Property3="USA"},
    new List2() {Property1="666",Property2="Toyota" ,Property3="USA"},
};

我需要获取 mylist1 和 mylist2 中的匹配项。匹配应该只发生在 Property1 和 Property2 上。比较时可以忽略 mylist2 中的 Property3。

目前我使用

var matchingCodes = mylist1.Where(l1 => mylist2.Any(l2 => (l2.Property1 == l1.Property1 && l2.Property2==l1.Property2))).ToList();

效果很好。但是有没有更好/最快的方法来做到这一点?

我可以将 List1 更改为任何其他类型。但不是 List2。

【问题讨论】:

  • 来格式一致的缩进
  • LINQ 很少是性能最好的选项

标签: c# performance linq collections


【解决方案1】:

你也可以加入:

var query= from l in mylist1
           join e in mylist2 on new {Property1=l.Property1,Property2=l.Property2} equals new {Property1=e.Property1,Property2=e.Property2}
           select l;

【讨论】:

  • 您可以省略“名称”,我猜它的可读性更高。 new {Property1=l.Property1,Property2=l.Property2} => new {l.Property1, l.Property2}
  • 谢谢@CSharpie,我知道这一点,但如果这两个类的属性名称不同,那么有必要这样做。我假设那些不是真实姓名,但如果他们的名字匹配,是的,匿名类型中的属性名可以省略
  • 你是对的,这让你的答案更防水。
  • 我用我的方法和上面建议的方法做了一个快速的基准测试。我的方法 - 54 毫秒。建议 - 116 毫秒
【解决方案2】:

在 Linq 中相对较快或至少比您的方法快的最简单方法是使用 JoinGroupJoin,如下所示:

List<List1> matchingCodes = mylist1.GroupJoin(mylist2,

               l1 => new { l1.Property1, l1.Property2 },// Define how the key from List1 looks like
               l2 => new { l2.Property1, l2.Property2 },// Define how the key from List2 looks like

               // Define what we select from the match between list1 and list2
               (l1Item, l2Items) => l1Item).ToList();

简单地说,这会创建两个字典,然后将它们连接在一起。

GroupJoin 在这里效果更好,因为它为您提供 List1 中的项目和 list2 中的所有匹配项。

一个普通的Join 会从 List2 中的每个匹配项返回相同的项目。

另见Enumerable.GroupJoin (C# Reference)

注意这相当于@octavioccl's answer。 此示例还假设,两个类的属性名称相同。如果它们不是你必须修改它们的键选择器,如下所示:

l1 => new { A=l1.Foo, B=l1.Bar},
l2 => new { A=l2.Herp, B=l2.Derp},

【讨论】:

  • 我用我的方法和上面建议的方法做了一个快速的基准测试。我的方法 - 54 毫秒。建议 - 112 毫秒
  • @Gokul 这真的取决于这些列表中的项目数量。如果数量很小,则连接可能在执行其工作之前创建这些哈希表的开销更大。我的回答还假设您的列表包含更多的项目,否则要求更高性能的解决方案没有多大意义。
  • mylist1 可能包含超过 1000 - 10,000 个项目。但 mylist2 将有 1-10 个项目。
  • @Gokul 那么没有太多的性能可以提高,因为即使您尝试了迭代次数也很少。如果两个列表都是 1000 多个项目,那将是完全不同的。我希望您使用 StopWatch 而不是 DateTime.Now 进行性能测试,因为我无法重现如此大的差异。
  • @Gokul 只需测试一次,list2 中没有一个匹配项,您应该会看到巨大的差异。
【解决方案3】:

您正在尝试对 LINQ 中的数据列表执行 set 操作。您可以使用四个 LINQ 函数调用来使当前代码更简洁。这些操作是:

  • 除了
  • 联合
  • 相交
  • 不同

你要找的是 Intersect

返回在两个单独的集合中发现的相同值的集合 Source

最后,如果您总是要使用这些特定属性来检测相等性和/或唯一性,您将需要为 List1 和/或 List2 类覆盖 Equals。这将取决于谁被认为在 Intersect 的左侧(. 之前的变量)以及谁在 Intersect 的右侧(传递给函数的变量)。

Here is a SO answer 了解如何覆盖 Equals 函数,如果您不知道该怎么做。巧合的是,它还有一个Intersect 的例子。

【讨论】:

  • 对于 OP 案例,这些将​​需要自定义 IEqualityComparer&lt;T&gt; 实现。基于匿名类型的join解决方案更加简单灵活。
  • 我在看到评论之前正在编辑它。现在应该在那里。虽然我走的是平等路线。此外,有人问是否有“更好”的方法来做到这一点。这是高度自以为是的,因为它比所有join 答案更容易在代码中阅读。
【解决方案4】:

这两个属性都是字符串,因此您可以使用 这些属性的串联 键创建一个字典,其值为 实际项目

因此,对于其他列表中的每个项目,您只需在字典中查找其属性的串联,如果匹配,则与找到的项目进行比较

【讨论】:

  • 我无法在其他地方推荐,所以...最快 方法应该是这种方法,并且建议使用 Sets。集合更干净,但你有两种不同类型的对象 - 如果你可以绕过它 - 使用集合。只要记住覆盖 GetHashcode。我的建议应该尽可能快,并且不需要您对实际课程进行更改。我无法提供示例 - 我没有在该机器上安装 VS
猜你喜欢
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多