4

別のデータベースからデータをインポートしています。

私のプロセスは、リモートDBからList<DataModel>名前付きremoteDataにデータをインポートし、ローカルDBからList<DataModel>名前付きにデータをインポートしていlocalDataます。

次に、LINQ を使用して異なるレコードのリストを作成し、リモート DB から取得したデータと一致するようにローカル DB を更新できるようにします。このような:

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();

各コレクションは約 70,000 レコードをインポートし、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。ハッシュコードを実装すると、パフォーマンスが大幅に向上します。

MatchingDataCompaer2つのうちのほうが簡単なので、それを行います。

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の 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 に答える