C# 基于某些(不是全部)属性值比较列表内容时嵌套foreach的替代方法

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, >

作为本文的一部分,有人反复指出,我在使用类似于此的代码时遇到了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, > 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
  }
}