比较两个集合优化

时间:2013-12-13 10:19:28

标签: c# linq optimization collections

我需要在比较和操纵其值时优化代码。我有两个数据集合,比如集合A和集合B.在集合A中是所有现有数据的列表,在集合B中是合并现有数据和要添加的新数据。现在,我需要检查集合B中是否存在集合B,如果存在,我需要使用集合B的更新数据设置或修改集合A的特定数据。如果集合B的特定数据在A中不存在则我需要将它添加到集合A中。

这是我的代码。

  public CollectionA Save(List<CollectionB> collectionb)
  var collectionA = collectionA.GetAll().OrderByDescending(x => x.SortOrder);
 foreach (var b in collectionb)
            {
                if (b.Id == 0)
                {
                    a.Add(b);
                    continue;
                }
                foreach (var a in collectionA.Where(aa => aa.Id == b.Id))
                {
                    a.ZoneCode = b.ZoneCode;
                    a.SortOrder = b.SortOrder;
                    a.Point1 = b.Point1;
                    a.Point2 = b.Point2;
                    a.Point3 = b.Point3;
                    a.RangeSpread = b.RangeSpread;
                    a.MidpointDifferential = b.MidpointDifferential;
                    a.AnnualIncentivePercent = b.AnnualIncentivePercent;
                    a.AnnualIncentiveAmount = b.AnnualIncentiveAmount;
                    a.GradeMisc1 = b.GradeMisc1;
                    a.GradeMisc2 = b.GradeMisc2;
                    a.ZoneComment = b.ZoneComment;
                }
            }

2 个答案:

答案 0 :(得分:1)

所以你有2个大型列表,你想找到它们之间的变化,而不是真正地将每个人与每个人进行比较。

您应该创建2个词典并按其键值对其进行索引。 C#匿名类型非常适合这种情况,因为编译器已根据值生成GetHashCode和Equals。

这是一种相当天真的方法(因为它可以轻松抛出OutOfMemoryException,因为它可以完成内存中的所有操作),但它应该比仅仅比较A中的所有内容和B中的所有内容更好地执行更多。 这只会比较密钥相同的值:

class ListComparisonResult<TKey>
{
  public IList<TKey> NewKeys { get; private set; }
  public IList<TKey> OldKeys { get; private set; }
  public IList<TKey> ChangedKeys { get; private set; }

  public ListComparisonResult(IList<TKey> newKeys, IList<TKey> oldKeys, IList<TKey> changedKeys)
  {
    NewKeys = new ReadOnlyCollection<TKey>(newKeys);
    OldKeys = new ReadOnlyCollection<TKey>(oldKeys);
    ChangedKeys = new ReadOnlyCollection<TKey>(changedKeys);
  }
}

ListComparisonResult<TKey> GetChanges<TRow, TKey, TValues>(
  IEnumerable<TRow> collectionA, 
  IEnumerable<TRow> collectionB,  
  Func<TRow, TKey> keySelector, 
  Func<TRow, TValues> comparableValuesSelector)
{
  var byId = new
  {
    A = collectionA.ToDictionary(keySelector),
    B = collectionB.ToDictionary(keySelector),
  };

  var sameIds = new HashSet<TKey>(byId.A.Keys.Where(byId.B.ContainsKey));

  var changedIds = (from id in sameIds
                    let a = byId.A[id]
                    let b = byId.B[id]
                    where !comparableValuesSelector(a).Equals(comparableValuesSelector(b))
                    select id).ToList();

  var oldIds = byId.A.Keys.Where(id => !byId.B.ContainsKey(id)).ToList();
  var newIds = byId.B.Keys.Where(id => !byId.A.ContainsKey(id)).ToList();
  return new ListComparisonResult<TKey>(newIds, oldIds, changedIds);
}

这是如何使用的:

var r = new Random();
var collectionA = (from id in Enumerable.Range(0, 1000000)
                   select new
                   {
                     ID = id, 
                     Value1 = r.Next(1, 3),
                     Value2 = r.Next(0, 1) == 1,
                   }).ToList();

var collectionB = (from id in Enumerable.Range(58945, 1000000)
                   select new
                   {
                     ID = id, 
                     Value1 = r.Next(1, 3),
                     Value2 = r.Next(0, 1) == 1,
                   }).ToList();

var timer = Stopwatch.StartNew();
var changes = GetChanges(collectionA, collectionB, t => t.ID, t => new{t.Value1, t.Value2});
timer.Stop();
Console.WriteLine(new
{
  changedIds = changes.ChangedKeys.Count, 
  newIds = changes.NewKeys.Count, 
  oldIds = changes.OldKeys.Count, 
  timer.ElapsedMilliseconds
});

答案 1 :(得分:0)

使用.FirstOrDefault方法查找具有相同ID的项目。如果该项目存在,请将其从主集合中删除,然后添加新(/更新)项目。如果没有匹配的ID,只需添加新项。

foreach (var b in collectionB)
{
   var itemToUpdate = collectionA.FirstOrDefault(a => a.Id == b.Id);
   if (itemToUpdate != null)
   {
      collectionA.Remove(itemToUpdate);
   }
   collectionA.Add(b);
}

编辑:

如果您的列表包含具有唯一ID的项目,则可以使用.SingleOrDefault方法。不同的是,如果有多个具有相同ID的项目,.SingleOrDefault将抛出异常。