我有两个对象列表,它们有一个名为OrderNumber的公共属性。
第一份清单有大约20000项,第二份清单有150万项。
我需要一种有效的方法来从列表1中找到与列表2中不匹配的项目。我目前正在使用Linq,计算解决方案需要20多分钟。我在网上找不到有效的解决方案。
到目前为止我的代码
notmatched.AddRange(List1.Where(l1=> !list2.Select(l2=> l2.OrderNumber).Contains(l1.OrderNumber)).Select(l1 => new SomeObj
{
OrderNumber = l1.OrderNumber
}));
使用Linq提供的内置Except扩展足够快,可以提供自定义的IEqualityComparer。我的实现可能不适用于您的用例,但考虑到firstList中有150万个Poco类,secondList中有20k个,它在1秒内执行。
IEqualityComparer,Linq的文档,除外
DotnetFiddle-减少数字以绕过内存限制
// Classes used in test:
public interface IOrderNumber
{
string OrderNumber { get; set; }
}
public class Poco: IOrderNumber
{
public string OrderNumber { get; set; }
}
public class Podo: IOrderNumber
{
public string OrderNumber {get;set;}
}
public class DataEqualityComparer : IEqualityComparer<IOrderNumber>
{
public bool Equals(IOrderNumber p1, IOrderNumber p2)
{
var equal = GetHashCode(p1) == GetHashCode(p2);
return equal;
}
public int GetHashCode(IOrderNumber p1)
{
if (p1 == null)
return -1;
int hCode = p1.OrderNumber.GetHashCode();
return hCode.GetHashCode();
}
}
... then your code would look like this:
var firstList = Enumerable.Range(1, 1500000).Select(x => new Poco { OrderNumber = x.ToString() }).ToList();
var secondList = Enumerable.Range(50, 20000).Select(x => new Podo { OrderNumber = x.ToString() }).ToList();
Stopwatch sw = Stopwatch.StartNew();
var result = firstList.Except(secondList, new DataEqualityComparer()).ToList();
sw.Stop();
Console.WriteLine($"Duration: {sw.Elapsed:G}");
这是您的解决方案的一个更简化的版本,它使用更少的循环来完成这项工作,但我不确定这是否会使过程更快,请告诉我它是否能完成
notmatched.AddRange(List1.Where(l1=> !list2.Any(l2=> l2.OrderNumber == l1.OrderNumber).Select(l1 => new SomeObj
{
OrderNumber = l1.OrderNumber
}));
使用LinqExcept
来比较对象。必须为复杂对象实现IEquatable接口。
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var first = new List<Poco> { new Poco {OrderNumber = "test", Orderid = 1 }, new Poco {OrderNumber = "test", Orderid = 1 } };
var second = new List<Poco> { new Poco {OrderNumber = "test", Orderid = 1 }, new Poco {OrderNumber = "test", Orderid = 1 } };
first.Except(second).Dump();
second.Except(first).Dump();
if ( !first.Except(second).Any() && !second.Except(first).Any())
{
Console.Write("first and second are equal");
} else {
Console.Write("equality failed");
}
}
}
public class Poco : IEquatable<Poco>
{
public string OrderNumber { get; set; }
public int Orderid {get; set;}
public bool Equals(Poco other)
{
if (other == null )
return false;
return this.Orderid == other.Orderid && this.OrderNumber == other.OrderNumber;
}
public override bool Equals(Object obj)
{
if (obj == null)
return false;
Poco poco = obj as Poco;
if (poco == null)
return false;
else
return Equals(obj);
}
public override int GetHashCode() => (Orderid, OrderNumber).GetHashCode();
}