2017-11-29 1 views
2

私は2つのリストのデータセットを持っていますが、どちらのリストでも一貫性のあるIDがありますが、異なるかもしれないし違うかもしれない他のプロパティもあります。 1つ以上のプロパティに基づいて異なるものを最も効率的に取得するにはどうすればよいですか?C#でオブジェクトの2つの大きなリストのプロパティを効率的に比較するにはどうすればよいですか?

私の通常のアプローチはこれに沿ったものでした。私のオブジェクトは次のように設定されています:

public class Person 
     { 
      public int ID { get; set; } 
      public string Name { get; set; } 
      public int Age { get; set; } 

      public bool IsEqual(Person other) 
      { 
       if (Name != other.Name) 
       { 
        return false; 
       } 
       if (Age != other.Age) 
       { 
        return false; 
       } 
       return true; 
      } 
     } 

ここで、IsEqualコンパレータを使用して、それと同等のオブジェクトを比較します。修正の人を見つけるための私の方法は以下のようであるその後、

そして:私のデータセットでは

public static List<Person> FindModifiedPeople(List<Person> listA, List<Person> listB) 
     { 
      var modifiedPeople = new List<Person>(); 
      foreach (var personA in listA) 
      { 
       var matchingPerson = listB.FirstOrDefault(e => e.ID == personA.ID); 
       if (matchingPerson == null) 
       { 
        continue; 
       } 

       if (!personA.IsEqual(matchingPerson)) 
       { 
        modifiedPeople.Add(personA); 
       } 
      } 
      return modifiedPeople; 
     } 

、私はListBの中にいる人ではなくLISTA気にしないので、私はをループする必要はありません。両方のリスト。リストBの要素をlistAでチェックするだけで、リストAの要素を使って変更された人のリストを返すことができます。

このアプローチは、合理的に小さいリストではうまくいきましたが、現在は約160,000人のリストが2つあり、このアプローチには数分かかります。この方法をより効率的にする方法はありますか?それでも必要なものを返すのですか?

+4

あなたはありますかリストを使用するには?あなたがその人のIDを持っていれば、辞書のようなものにそれらを保管することができないでしょうか? –

+2

本当に一緒にリストを比較する必要はありますか?なぜ、オブジェクトが更新されていればそのオブジェクト内を追跡し、それをブール値のプロパティとして公開しないでください(つまり、 '' person.IsDirty')? – DavidG

+7

私は今、 'person.IsDirty'が最良の命名規則ではないかもしれないことに気づいています... – DavidG

答えて

3

あなたのリストをDictionary<int, Person>に変更することができれば、その人物のIDをキーとして使用することができます。これはO(n^2)ではなく、O(n)で実行されます。

public static List<Person> FindModifiedPeople(Dictionary<int, Person> dictA, Dictionary<int, Person> dictB) 
{ 
    var modifiedPeople = new List<Person>(); 
    foreach (var personA in dictA) 
    { 
     Person matchingPerson; 
     if(dictB.TryGetValue(personA.Key, out matchingPerson)) 
     { 
      if (!personA.Value.IsEqual(matchingPerson)) 
      { 
       modifiedPeople.Add(personA.Value); 
      } 
     } 
    } 
    return modifiedPeople; 
} 

また、あなたがそれを必要とするものに応じてだけでなく、他の辞書にリストから戻り値の型を変更することができます。

EDIT

@maccetturaが彼のコメントで指摘したように、あなたは本当に、equalsメソッドに建てオーバーライドする必要があります。それはあなたのコードをこのように見えるようにします。

public override bool Equals(Object obj) 
{ 
    if (obj == null || GetType() != obj.GetType()) 
     return false; 

    var otherPerson = (Person)obj; 

    if (Name != otherPerson.Name) 
    { 
     return false; 
    } 
    if (Age != otherPerson.Age) 
    { 
     return false; 
    } 
    return true; 
} 

これにより、独自のコードではなく、デフォルトのEqualsメソッドを使用することを想定しているコードでもコードを使用できます。

+0

「Equals()」をオーバーライドしてもいいかもしれないと思う(OPにいくつかのよりよいプラクティスを与えるかもしれない) – maccettura

+1

ええ。私は何も言わなかっただけであなたのコメントにそれを置いたが、私はここにそれを追加します。 –

+0

うわー!それは大きな違いをもたらしました!それは素晴らしいです、ありがとう。 また、私はEquals()をオーバーライドする必要があることを知っています。私の同僚は、Equalsのオーバーライドを既に追加しています。なぜなら、IDを比較してプロパティを気にしないときにtrueを返すためです。したがって、オーバーライドは次のようなものです。 public override bool等しい(他人){ if(ID!= other.ID){ false false; } がtrueを返します。 } 明らかに私のケースでは、Equalsに具体的にプロパティを比較させてもらいたいと思います。私は本当にこのジレンマを解決する方法がわからなかったので、私は自分の方法を別の方法にしました。 – RamblerToning

3

比較がボトルネックかどうか確認してください。そこ

var matchingPerson = listB.FirstOrDefault(e => e.ID == personA.ID); 

、あなたはforeachループに結合されたO(n)と、のlogartihmic複雑で検索を行っているの総複雑さを与える:私はこの問題は、あなたがこの行で行う検索を形成くると思いますO(n^2)である。その代わりに、辞書を先に作成することができますが、時間がかかりますが、参照がはるかに高速です。辞書はキーとしてIDを持つ必要があり、簡単にforeachループの前に、このように作成することができます。その後

var dictB = listB.ToDictionary(p => p.ID); 

、あなたのルックアップは、このように、はるかに高速になります:

Person matchingPerson; 
    if (dictB.TryGetValue(personA.ID, out matchingPerson)) 
    { 
     if (!personA.IsEqual(matchingPerson)) 
     { 
      modifiedPeople.Add(personA); 
     } 
    } 
+0

あなたは絶対に正しいです、FirstOrDefaultは確かに比較器ではなくボトルネックでした。私は辞書をリストから外すように変更しました(私は他の目的のためにリストが必要です)。また、そのオーバーヘッドを含めても検索は大幅に高速です。あなたとマイケル・シャープの両方に、もし私が答えることができれば答えることができます! – RamblerToning

関連する問題