4

我正在从另一个数据库导入数据。

我的过程是将远程数据库中的数据导入List<DataModel>命名remoteData数据库,并将本地数据库中的数据导入List<DataModel>命名数据库localData

然后我使用 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 条记录,3 个 LINQ 操作中的每一个都需要 5 到 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;
    }
}
4

1 回答 1

5

通过使用恒定的哈希码,您已经破坏了性能。这是 Intersect 使用的内部代码(通过反编译器获得)

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;
        }
    }
}

您可能会使用MatchingDataComparerin 中的 hashcode 函数OutdatedDataComparer,它可能不是“最佳”哈希码1,但它将是“合法”的2,并且比硬编码的 0 快得多。


1. 或者它可能是,我不确定我将如何包含第 3 个&&条件
2. 如果a.Equals(b) == true那么a.GetHashCode() == b.GetHashCode().
如果a.Equals(b) == false那时a.GetHashCode() == b.GetHashCode() || a.GetHashCode() != b.GetHashCode()

于 2013-11-01T21:41:03.053 回答