2013-11-01 27 views
2

我正在从另一个数据库导入数据。自定义对象的大集合Intersection()和Except()速度太慢

我的过程是进口从远程数据库的数据为List<DataModel>名为remoteData和进口也从本地DB数据到一个名为localDataList<DataModel>

然后,我使用LINQ创建不同的记录列表,以便我可以更新本地数据库以匹配从远程数据库中提取的数据。就像这样:

var outdatedData = this.localData.Intersect(this.remoteData, new OutdatedDataComparer()).ToList(); 

我然后使用LINQ创建一个记录列表,在remoteData不复存在,但在localData确实存在,使我从本地数据库中删除。

像这样:

var oldData = this.localData.Except(this.remoteData, new MatchingDataComparer()).ToList(); 

我然后使用LINQ做上述相反的新数据添加到本地数据库。

像这样:

var newData = this.remoteData.Except(this.localData, new MatchingDataComparer()).ToList(); 

每个收集进口约70K的记录,每5之间的3 LINQ操作起跳 - 10分钟完成。 如何让这个更快?

这里是集合使用对象:

internal class DataModel 
{ 
     public string Key1{ get; set; } 
     public string Key2{ get; set; } 

     public string Value1{ get; set; } 
     public string Value2{ get; set; } 
     public byte? Value3{ get; set; } 
} 

用于检查过时的记录的比较器:

class OutdatedDataComparer : IEqualityComparer<DataModel> 
{ 
    public bool Equals(DataModel x, DataModel y) 
    { 
     var e = 
      string.Equals(x.Key1, y.Key1) && 
      string.Equals(x.Key2, y.Key2) && (
       !string.Equals(x.Value1, y.Value1) || 
       !string.Equals(x.Value2, y.Value2) || 
       x.Value3 != y.Value3 
       ); 
     return e; 
    } 

    public int GetHashCode(DataModel obj) 
    { 
     return 0; 
    } 
} 

用来寻找新老记录的比较器:

internal class MatchingDataComparer : IEqualityComparer<DataModel> 
{ 
    public bool Equals(DataModel x, DataModel y) 
    { 
     return string.Equals(x.Key1, y.Key1) && string.Equals(x.Key2, y.Key2); 
    } 

    public int GetHashCode(DataModel obj) 
    { 
     return 0; 
    } 
} 
+11

你应该真的* *实现的哈希码。 –

+4

哈希码用于查找哈希表中的对象,这可能是Except和Intersect在内部用来查找匹配对象的原因。通过返回一个常数值,所有对象将具有相同的位置,并且匹配搜索将降级为所有候选中的线性搜索。您需要基于用于相等的属性正确实现'GetHashCode'。 – Lee

+0

正确。我添加了一个哈希码,并且该操作需要一秒钟!谢谢。 – Theo

回答

3

通过拥有恒定的哈希码,您已经破坏了性能。这里是内部代码相交用途(通过反编译得到)

public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) 
{ 
    if (first == null) 
    { 
     throw Error.ArgumentNull("first"); 
    } 
    if (second == null) 
    { 
     throw Error.ArgumentNull("second"); 
    } 
    return Enumerable.IntersectIterator<TSource>(first, second, comparer); 
} 

private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) 
{ 
    Set<TSource> set = new Set<TSource>(comparer); 
    foreach (TSource current in second) 
    { 
     set.Add(current); 
    } 
    foreach (TSource current2 in first) 
    { 
     if (set.Remove(current2)) 
     { 
      yield return current2; 
     } 
    } 
    yield break; 
} 

看到它是用Set内,如果实现的哈希码将大大提高它的性能。

MatchingDataCompaer是两个更容易,所以我会为你做一个。

internal class MatchingDataComparer : IEqualityComparer<DataModel> 
{ 
    public MatchingDataComparer() 
    { 
     comparer = StringComparer.Ordnal; //Use whatever comparer you want. 
    } 

    private readonly StringComparer comparer; 

    public bool Equals(DataModel x, DataModel y) 
    { 
     return comparer.Equals(x.Key1, y.Key1) && comparer.Equals(x.Key2, y.Key2); 
    } 

    //Based off of the advice from http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode 
    public int GetHashCode(DataModel obj) 
    {  
     unchecked // Overflow is fine, just wrap 
     { 
      int hash = 17; 
      hash = hash * 23 + comparer.GetHashCode(obj.Key1); 
      hash = hash * 23 + comparer.GetHashCode(obj.Key2); 
      return hash; 
     } 
    } 
} 

你可能会使用来自MatchingDataComparer的哈希码功能OutdatedDataComparer,它可能不是“optimial”散列码,但它会是一个“合法”的之一,并会比硬编码0


1.或者,它可能会快很多,我不知道我怎么会包括3 &&条件
2.如果a.Equals(b) == true然后a.GetHashCode() == b.GetHashCode()
然后 如果a.Equals(b) == falsea.GetHashCode() == b.GetHashCode() || a.GetHashCode() != b.GetHashCode()

+0

谢谢你的详细解释斯科特!我实现了散列码,操作速度很快。 – Theo