C# 基于某些(不是全部)属性值比较列表内容时嵌套foreach的替代方法
作为本文的一部分,有人反复指出,我在使用类似于此的代码时遇到了O(n^2)问题C# 基于某些(不是全部)属性值比较列表内容时嵌套foreach的替代方法,c#,foreach,big-o,C#,Foreach,Big O,作为本文的一部分,有人反复指出,我在使用类似于此的代码时遇到了O(n^2)问题 public class Foo { public string IdentityValue {get;set;} public string Prop1 {get;set;} public string Prop2 {get;set;} } List<Foo> itemSet1 = GenerateLargeItemSet(); //makes a large list, >
public class Foo
{
public string IdentityValue {get;set;}
public string Prop1 {get;set;}
public string Prop2 {get;set;}
}
List<Foo> itemSet1 = GenerateLargeItemSet(); //makes a large list, > 5000 items for example
List<Foo> itemSet2 = GenerateLargeItemSet();
foreach (var itemFromSet1 in itemSet1)
{
//does a corresponding item exist in itemSet2?
var itemSet2Item = itemSet2.FirstOrDefault(i => i.IdentityValue == itemFromSet1.IdentityValue);
if (itemSet2Item != null)
{
//do stuff to create item in the persistent store
}
else
{
//do stuff to update item in the persistent store
}
}
公共类Foo
{
公共字符串标识值{get;set;}
公共字符串Prop1{get;set;}
公共字符串Prop2{get;set;}
}
List itemSet1=GenerateLargeItemSet()//制作一个大列表,例如>5000项
List itemSet2=GenerateLargeItemSet();
foreach(itemSet1中的var itemFromSet1)
{
//itemSet2中是否存在相应的项?
var itemSet2Item=itemSet2.FirstOrDefault(i=>i.IdentityValue==itemFromSet1.IdentityValue);
if(itemSet2Item!=null)
{
//在持久性存储中执行创建项的操作
}
其他的
{
//执行更新持久存储中项目的操作
}
}
抛开字符串比较和并行化的考虑,是否有一种便宜且通用的方法(对象可能是T类型,而Identity属性可能是其他类型)来减少这种O(n^2)性质?使用哈希集可以提高性能
List<Foo> itemSet1 = GenerateLargeItemSet(); //makes a large list, > 5000 items for example
HashSet<Foo> itemSet2 = new HashSet<Foo>(GenerateLargeItemSet());
foreach (var itemFromSet1 in itemSet1)
{
//does a corresponding item exist in itemSet2?
if (itemSet2.Contains(itemFromSet1))
{
//do stuff to update item in the persistent store
}
//do stuff to create item in the persistent store
}
List itemSet1=generateArgeItemSet()//制作一个大列表,例如>5000项
HashSet itemSet2=新的HashSet(generatelargetimset());
foreach(itemSet1中的var itemFromSet1)
{
//itemSet2中是否存在相应的项?
if(itemSet2.Contains(itemsfromset1))
{
//执行更新持久存储中项目的操作
}
//在持久性存储中执行创建项的操作
}
解决方案之一是使用具有复杂
List itemSet1=generateArgeItemSet()//制作一个大列表,例如>5000项
List itemSet2=GenerateLargeItemSet();
//O(n)
var joinedSet=itemSet1.Join(itemSet2,s1=>s1.IdentityValue,s2=>s2.IdentityValue,(f1,f2)=>f1).ToList();
//O(n)
foreach(joinedSet中的var joinedItem)
{
//在持久性存储中执行创建项的操作
}
//O(n)
var unjoinedSet=itemSet1.Except(joinedSet);
//O(n)
foreach(var unjoinedItem在unjoinedSet中)
{
//执行更新持久存储中项目的操作
}
提高数据库查询速度的一种众所周知的方法是创建索引。同样的原理也适用于此。但什么是指数?它只是一个允许快速搜索的数据结构。在BCL中,这种结构称为字典。所以你可以使用这样的东西,它的时间复杂度为O(N)
如果该值在集合中是唯一的
var item2Index = itemSet2.ToDictionary(item => item.IdentityValue);
如果不是
var item2Index = itemSet2.GroupBy(e => e.IdentityValue)
.ToDictionary(g => g.Key, g => g.First());
然后
foreach (var itemFromSet1 in itemSet1)
{
//does a corresponding item exist in itemSet2?
Foo itemSet2Item;
if (!item2Index.TryGetValue(itemFromSet1.IdentityValue, out itemSet2Item))
{
//do stuff to create item in the persistent store
}
else
{
//do stuff to update item in the persistent store
}
}
如果您只想检查第二个集合中的重复项,但实际上不需要重复项,那么您可以使用简单的哈希集
(另一个用于快速查找的BCL数据结构)
var item2Keys=newhashset(itemSet2.Select(e=>e.IdentityValue));
foreach(itemSet1中的var itemFromSet1)
{
//itemSet2中是否存在相应的项?
如果(!item2Keys.Contains(itemFromSet1.IdentityValue))
{
//在持久性存储中执行创建项的操作
}
其他的
{
//执行更新持久存储中项目的操作
}
}
您可以先创建字典
,然后使用它提供的几乎O(1)
:
List itemSet1=generateArgeItemSet()//制作一个大列表,例如>5000项
Dictionary itemSet2=GenerateArgeItemSet().ToDictionary(i=>i.IdentityValue);
//O(N)
foreach(itemSet1中的var itemFromSet1)
{
//O(1)
如果(!itemSet2.ContainsKey(itemFromSet1.IdentityValue))
{
//在持久性存储中执行创建项的操作
}
其他的
{
//执行更新持久存储中项目的操作
}
}
您可以创建一组列表。这样你就得到了O(n)
。你们中的任何一个能提供一个解释你的意思的答案吗?与其将itemSet2
作为一个列表,不如将其设置为一个真正的集合,这样的查找将是O(1)
而不是O(n)
@ndn-它是一个进入包含for循环的方法的列表。如果你的意思是这样的,我可以按照瓦迪姆下面的建议进行转换。是的,如果你转换它,复杂性将保持O(n)
。我不是在比较对象。我正在比较对象上的属性值以确定它们是否相等。@StingyJack这只意味着如果对象没有基于该属性定义相等,则需要为集合提供一个比较器。@StingyJack或者,您只需将选择器的结果存储在集合中,并在搜索集合之前对对象执行选择器。不管怎样,都是微不足道的适应。
foreach (var itemFromSet1 in itemSet1)
{
//does a corresponding item exist in itemSet2?
Foo itemSet2Item;
if (!item2Index.TryGetValue(itemFromSet1.IdentityValue, out itemSet2Item))
{
//do stuff to create item in the persistent store
}
else
{
//do stuff to update item in the persistent store
}
}
var item2Keys = new HashSet<string>(itemSet2.Select(e => e.IdentityValue));
foreach (var itemFromSet1 in itemSet1)
{
//does a corresponding item exist in itemSet2?
if (!item2Keys.Contains(itemFromSet1.IdentityValue))
{
//do stuff to create item in the persistent store
}
else
{
//do stuff to update item in the persistent store
}
}
List<Foo> itemSet1 = GenerateLargeItemSet(); //makes a large list, > 5000 items for example
Dictionary<string, Foo> itemSet2 = GenerateLargeItemSet().ToDictionary(i => i.IdentityValue);
//O(N)
foreach (var itemFromSet1 in itemSet1)
{
//O(1)
if (!itemSet2.ContainsKey(itemFromSet1.IdentityValue))
{
//do stuff to create item in the persistent store
}
else
{
//do stuff to update item in the persistent store
}
}