C# 按特定 属性 比较两个大型项目列表
C# comparing two large lists of items by a specific property
我有两个很大的项目列表,class 看起来像这样(两个列表的类型相同):
public class Items
{
public string ItemID { get; set; }
public int QuantitySold { get; set; }
}
var oldList = new List<Items>(); // oldList
var newList = new List<Items>(); // new list
旧列表包含来自数据库的项目,新列表表示从 API;
中获取的项目
两个列表都可以非常大,每个列表都有 10000 多个项目(总共 20000 个)
我需要将 newList 中的项目与 "oldList" 中的项目进行比较,看看哪些项目具有相同的 itemID 值,哪些具有不同的 "QuantitySold" 值,哪些具有不同的 [=44] =] 值应该存储在名为 "differentQuantityItems".
的第三个列表中
我可以简单地执行 double foreach 列表并比较值,但是由于两个列表都很大,所以 double foreach 循环的性能很糟糕,我做不到...
有人可以帮我解决这个问题吗?
@YamamotoTetsua 我已经在使用 IEqualityComparer 来获得所需的结果,但是它没有给出我期望的结果。这就是为什么......我有一个看起来像这样的第一个 IEqualityComparer:
public class MissingItemComparer : IEqualityComparer<SearchedUserItems>
{
public static readonly IEqualityComparer<SearchedUserItems> Instance = new MissingItemComparer();
public bool Equals(SearchedUserItems x, SearchedUserItems y)
{
return x.ItemID == y.ItemID;
}
public int GetHashCode(SearchedUserItems x)
{
return x.ItemID.GetHashCode();
}
}
此 IEqualityComparer 的用法基本上为我提供了 newList 中不存在于我的数据库中的项目,如下所示:
var missingItems= newItems.Except(competitor.SearchedUserItems.ToList(), MissingItemComparer.Instance).ToList();
现在,在此列表中,我将获得 API 中的新项目列表,但不存在于我的数据库中...
第二个 IEqualityComparer 基于新旧列表中不同的 QuantitySold:
public class ItemsComparer : IEqualityComparer<SearchedUserItems>
{
public static readonly IEqualityComparer<SearchedUserItems> Instance = new ItemsComparer();
public bool Equals(SearchedUserItems x, SearchedUserItems y)
{
return (x.QuantitySold == y.QuantitySold);
}
public int GetHashCode(SearchedUserItems x)
{
return x.ItemID.GetHashCode();
}
}
用法示例:
var differentQuantityItems = newItems.Except(competitor.SearchedUserItems.ToList(), ItemsComparer.Instance).ToList();
这两个相等比较器的问题在于,第一个将例如 return 缺少这些 itemID:
123124124
123124421
512095902
我的 oldList 中确实缺少它们...但是第二个 IEQualityComparer 也会 return 这些项目作为 differentQuantity 项目,它们确实是,但在 oldList 中不存在。所以他们不应包含在第二个列表中。
从大 O 复杂度的角度来看,仅比较嵌套 for 循环中的列表将在 O(n*m) 的 class 中,n 是数据库中列表的大小,m 是从 API 中获取的列表的大小。
你可以做些什么来提高你的表现是对两个列表进行排序,这将花费 O(n log(n) + m log(m)),然后你可以在 O(n + m) 中找到新项目。因此,您的算法的整体复杂性将在 O(n log(n) + m log[ 的 class 中=30=](m)).
Here's an idea 的时间,将二次解与超线性解进行比较。
您可以考虑将 Except 子句与自定义编写的 IEqualityComparer
结合使用,如下所示
var oldList = new List<Item>(); // oldList
var newList = new List<Item>(); // new list
var distinctList = newList.Except(oldList,new ItemEqualityComparer()).ToList();
class ItemEqualityComparer : IEqualityComparer<Item>
{
public bool Equals(Item i1, Item i2)
{
if (i1.ItemID == i2.ItemID && i1.QuantitySold != i2.QuantitySold)
return false;
return true;
}
public int GetHashCode(Item item)
{
return item.ItemID.GetHashCode();
}
}
public class Item
{
public string ItemID { get; set; }
public int QuantitySold { get; set; }
}
这是 LINQ 的完美候选 Join:
var differentQuantityItems =
(from newItem in newList
join oldItem in oldList on newItem.ItemID equals oldItem.ItemID
where newItem.QuantitySold != oldItem.QuantitySold
select newItem).ToList();
这将 return 具有不同 QuantitySold 的相应旧项目的所有新项目。如果您还想包含没有对应旧项目的新项目,请使用 left outer join:
var differentQuantityItems =
(from newItem in newList
join oldItem in oldList on newItem.ItemID equals oldItem.ItemID into oldItems
from oldItem in oldItems.DefaultIfEmpty()
where oldItem == null || newItem.QuantitySold != oldItem.QuantitySold
select newItem).ToList();
在这两种情况下,连接运算符都用于快速关联具有相同 ItemID 的项目。然后您可以比较 QuantitySold 或任何其他属性。
此代码将在不到一秒内 运行,即使根本没有匹配项(如果 所有内容 都匹配,也不到一秒)。
它将 return 存在于两个列表中的所有项目(即相同 ItemID
)但 QuantitySold
.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp5
{
class Program
{
public class Items
{
public string ItemID { get; set; }
public int QuantitySold { get; set; }
}
static void Main(string[] args)
{
// Sample data
var oldList = new List<Items>();
oldList.AddRange(Enumerable.Range(0, 20000).Select(z => new Items() { ItemID = z.ToString(), QuantitySold = 4 }));
var newList = new List<Items>();
newList.AddRange(Enumerable.Range(0, 20000).Select(z => new Items() { ItemID = z.ToString(), QuantitySold = 5 }));
var results = oldList.Join(newList,
left => left.ItemID,
right => right.ItemID,
(left, right) => new { left, right })
.Where(z => z.left.QuantitySold != z.right.QuantitySold).Select(z => z.left);
Console.WriteLine(results.Count());
Console.ReadLine();
}
}
}
使用 z.left
意味着只有 一个 项目将被 returned - 如果你想要旧的和新的,而不是使用:
var results = oldList.Join(newList,
left => left.ItemID,
right => right.ItemID,
(left, right) => new { left, right })
.Where(z => z.left.QuantitySold != z.right.QuantitySold)
.Select(z => new[] { z.left, z.right })
.SelectMany(z => z);
我有两个很大的项目列表,class 看起来像这样(两个列表的类型相同):
public class Items
{
public string ItemID { get; set; }
public int QuantitySold { get; set; }
}
var oldList = new List<Items>(); // oldList
var newList = new List<Items>(); // new list
旧列表包含来自数据库的项目,新列表表示从 API;
中获取的项目两个列表都可以非常大,每个列表都有 10000 多个项目(总共 20000 个)
我需要将 newList 中的项目与 "oldList" 中的项目进行比较,看看哪些项目具有相同的 itemID 值,哪些具有不同的 "QuantitySold" 值,哪些具有不同的 [=44] =] 值应该存储在名为 "differentQuantityItems".
的第三个列表中我可以简单地执行 double foreach 列表并比较值,但是由于两个列表都很大,所以 double foreach 循环的性能很糟糕,我做不到...
有人可以帮我解决这个问题吗?
@YamamotoTetsua 我已经在使用 IEqualityComparer 来获得所需的结果,但是它没有给出我期望的结果。这就是为什么......我有一个看起来像这样的第一个 IEqualityComparer:
public class MissingItemComparer : IEqualityComparer<SearchedUserItems>
{
public static readonly IEqualityComparer<SearchedUserItems> Instance = new MissingItemComparer();
public bool Equals(SearchedUserItems x, SearchedUserItems y)
{
return x.ItemID == y.ItemID;
}
public int GetHashCode(SearchedUserItems x)
{
return x.ItemID.GetHashCode();
}
}
此 IEqualityComparer 的用法基本上为我提供了 newList 中不存在于我的数据库中的项目,如下所示:
var missingItems= newItems.Except(competitor.SearchedUserItems.ToList(), MissingItemComparer.Instance).ToList();
现在,在此列表中,我将获得 API 中的新项目列表,但不存在于我的数据库中...
第二个 IEqualityComparer 基于新旧列表中不同的 QuantitySold:
public class ItemsComparer : IEqualityComparer<SearchedUserItems>
{
public static readonly IEqualityComparer<SearchedUserItems> Instance = new ItemsComparer();
public bool Equals(SearchedUserItems x, SearchedUserItems y)
{
return (x.QuantitySold == y.QuantitySold);
}
public int GetHashCode(SearchedUserItems x)
{
return x.ItemID.GetHashCode();
}
}
用法示例:
var differentQuantityItems = newItems.Except(competitor.SearchedUserItems.ToList(), ItemsComparer.Instance).ToList();
这两个相等比较器的问题在于,第一个将例如 return 缺少这些 itemID:
123124124
123124421
512095902
我的 oldList 中确实缺少它们...但是第二个 IEQualityComparer 也会 return 这些项目作为 differentQuantity 项目,它们确实是,但在 oldList 中不存在。所以他们不应包含在第二个列表中。
从大 O 复杂度的角度来看,仅比较嵌套 for 循环中的列表将在 O(n*m) 的 class 中,n 是数据库中列表的大小,m 是从 API 中获取的列表的大小。
你可以做些什么来提高你的表现是对两个列表进行排序,这将花费 O(n log(n) + m log(m)),然后你可以在 O(n + m) 中找到新项目。因此,您的算法的整体复杂性将在 O(n log(n) + m log[ 的 class 中=30=](m)).
Here's an idea 的时间,将二次解与超线性解进行比较。
您可以考虑将 Except 子句与自定义编写的 IEqualityComparer
结合使用,如下所示
var oldList = new List<Item>(); // oldList
var newList = new List<Item>(); // new list
var distinctList = newList.Except(oldList,new ItemEqualityComparer()).ToList();
class ItemEqualityComparer : IEqualityComparer<Item>
{
public bool Equals(Item i1, Item i2)
{
if (i1.ItemID == i2.ItemID && i1.QuantitySold != i2.QuantitySold)
return false;
return true;
}
public int GetHashCode(Item item)
{
return item.ItemID.GetHashCode();
}
}
public class Item
{
public string ItemID { get; set; }
public int QuantitySold { get; set; }
}
这是 LINQ 的完美候选 Join:
var differentQuantityItems =
(from newItem in newList
join oldItem in oldList on newItem.ItemID equals oldItem.ItemID
where newItem.QuantitySold != oldItem.QuantitySold
select newItem).ToList();
这将 return 具有不同 QuantitySold 的相应旧项目的所有新项目。如果您还想包含没有对应旧项目的新项目,请使用 left outer join:
var differentQuantityItems =
(from newItem in newList
join oldItem in oldList on newItem.ItemID equals oldItem.ItemID into oldItems
from oldItem in oldItems.DefaultIfEmpty()
where oldItem == null || newItem.QuantitySold != oldItem.QuantitySold
select newItem).ToList();
在这两种情况下,连接运算符都用于快速关联具有相同 ItemID 的项目。然后您可以比较 QuantitySold 或任何其他属性。
此代码将在不到一秒内 运行,即使根本没有匹配项(如果 所有内容 都匹配,也不到一秒)。
它将 return 存在于两个列表中的所有项目(即相同 ItemID
)但 QuantitySold
.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp5
{
class Program
{
public class Items
{
public string ItemID { get; set; }
public int QuantitySold { get; set; }
}
static void Main(string[] args)
{
// Sample data
var oldList = new List<Items>();
oldList.AddRange(Enumerable.Range(0, 20000).Select(z => new Items() { ItemID = z.ToString(), QuantitySold = 4 }));
var newList = new List<Items>();
newList.AddRange(Enumerable.Range(0, 20000).Select(z => new Items() { ItemID = z.ToString(), QuantitySold = 5 }));
var results = oldList.Join(newList,
left => left.ItemID,
right => right.ItemID,
(left, right) => new { left, right })
.Where(z => z.left.QuantitySold != z.right.QuantitySold).Select(z => z.left);
Console.WriteLine(results.Count());
Console.ReadLine();
}
}
}
使用 z.left
意味着只有 一个 项目将被 returned - 如果你想要旧的和新的,而不是使用:
var results = oldList.Join(newList,
left => left.ItemID,
right => right.ItemID,
(left, right) => new { left, right })
.Where(z => z.left.QuantitySold != z.right.QuantitySold)
.Select(z => new[] { z.left, z.right })
.SelectMany(z => z);