11

私は2つのリストを持っています

ListA<Emp>両方ともListB<Emp> 1000レコードを持っています。

Emp従業員クラスのオブジェクトです。以下は私のEmployeeクラスです

public class Employee
{
    int ID = 0;
    string Name = String.Empty;
    string Dept = String.Empty;
    string Address = String.Empty;
    int Age = 0;
    string Email = String.Empty;
}

両方のリストが等しいかどうかを確認したいと思います。Empオブジェクトは異なる順序で配置できます。また、両方のリストにまったく同じ情報を持つ複数のEmpオブジェクトが存在する場合があります。それらも確認する必要があります。

リストを並べ替えて、を使用して比較してみましたSequenceEqual

Enumerable.SequenceEqual(ListA.OrderBy(s => s), ListB.OrderBy(s => s)

以下のエラーが発生します

At least one object must implement IComparable.
Exception Stack trace is as below 

   at System.Collections.Comparer.Compare(Object a, Object b)
   at System.Collections.Generic.ObjectComparer`1.Compare(T x, T y)
   at System.Linq.EnumerableSorter`2.CompareKeys(Int32 index1, Int32 index2)
   at System.Linq.EnumerableSorter`1.QuickSort(Int32[] map, Int32 left, Int32 right)
   at System.Linq.EnumerableSorter`1.Sort(TElement[] elements, Int32 count)
   at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()
   at System.Linq.Enumerable.SequenceEqual[TSource](IEnumerable`1 first, IEnumerable`1 second, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.SequenceEqual[TSource](IEnumerable`1 first, IEnumerable`1 second)

どうすればこれを実装できますか?また、リスト内のオブジェクトの数が1,000万に増える可能性があるため、これを行うための最速の方法を提供していただければ、より良いでしょう。ご協力いただきありがとうございます !

編集:すべての従業員は両方のリストに含まれている必要があります。順序は関係ありません。ただし、ListAに同じ従業員オブジェクトが5回含まれ(つまり、エントリが重複している)、ListBに従業員オブジェクトが4回含まれている場合、ListAとListBは等しくありません。

4

6 に答える 6

8

SequenceEqual カスタムで使用できますIEqualityComparer<Employee>

class EmployeeComparer : IEqualityComparer<Employee>
{
    public bool Equals(Employee x, Employee y)
    {
        if (x == null || y == null) return false;

        bool equals = x.ID==y.ID && x.Name == y.Name && x.Dept == y.Dept 
            && x.Address == y.Address && x.Age == y.Age && x.Email == y.Email;
        return equals;
    }

    public int GetHashCode(Employee obj)
    {
        if (obj == null) return int.MinValue;

        int hash = 19;
        hash = hash + obj.ID.GetHashCode();
        hash = hash + obj.Name.GetHashCode();
        hash = hash + obj.Dept.GetHashCode();
        hash = hash + obj.Address.GetHashCode();
        hash = hash + obj.Age.GetHashCode();
        hash = hash + obj.Email.GetHashCode();
        return hash;
    }
}

今はとても簡単です:

listA.SequenceEqual(ListB, new EmployeeComparer());

順序は重要ではなく、すべての従業員が両方のリストに含まれているかどうかだけを知りたい場合は、両方のリストHashSet<Employee>.SetEqualsに同じ人が含まれているかどうかを判断するために使用できます。

var empComparer =  new EmployeeComparer();
bool bothEqual = new HashSet<Employee>(ListA, empComparer)
      .SetEquals(new HashSet<Employee>(ListB, empComparer));
于 2013-01-09T14:00:10.513 に答える
5

最適な複雑さは O(N) HashSet を使用した次の実現です。

GetHashCode と Equals を実装したクラス:

public class Employee
{
    public int ID = 0;
    public string Name = String.Empty;
    public string Dept = String.Empty;
    public string Address = String.Empty;
    public int Age = 0;
    public string Email = String.Empty;

    public override int GetHashCode()
    {
        return
            ID.GetHashCode() ^
            (Name ?? String.Empty).GetHashCode() ^
            (Dept ?? String.Empty).GetHashCode() ^
            (Address ?? String.Empty).GetHashCode() ^
            Age.GetHashCode() ^
            (Email ?? String.Empty).GetHashCode()
            ;
    }
    public override bool Equals(object obj)
    {
        Employee other = obj as Employee;
        if (obj == null)
            return false;

        return ID == other.ID &&
                Name == other.Name &&
                Dept == other.Dept &&
                Address == other.Address &&
                Age == other.Age &&
                Email == other.Email;
    }
}

リストを比較する関数:

public static bool CompareLists(List<Employee> list1, List<Employee> list2)
{
    if (list1 == null || list2 == null)
        return list1 == list2;

    if (list1.Count != list2.Count)
        return false;
    Dictionary<Employee, int> hash = new Dictionary<Employee, int>();
    foreach (Employee employee in list1)
    {
        if (hash.ContainsKey(employee))
        {
            hash[employee]++;
        }
        else
        {
            hash.Add(employee, 1);
        }
    }

    foreach (Employee employee in list2)
    {
        if (!hash.ContainsKey(employee) || hash[employee] == 0)
        {
            return false;
        }
        hash[employee]--;
    }

    return true;
}
于 2013-01-09T14:10:42.043 に答える
1

リスト内の数値が非常に大きくなる (10M) 場合は、許容できるクエリ時間を取得するためにルックアップの並列化を検討する必要があります。

PLINQの使用を検討してください。

「等しい」という言葉の意味がもう少し明確になるとよいでしょう。等価性チェックはどのくらい複雑ですか? オブジェクトが同じであること、またはオブジェクトのが同じであることを確認していますか?

別の考慮事項はこれです。要素の数が多くなる場合、このチェックを .NET からデータベースに (おそらくストアド プロシージャとして) 移動することを検討できますか? そこでは、より効率的に実行されることがあります。

于 2013-01-09T13:58:15.703 に答える
1

reduce the list to a scalar type: int, string, ....

L1.Select(x => x.K).ToArray()

use the except method

L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray())

If the count of the resulting set is 0 then the List are equals

L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray()).Count()

All together

public class Program {
    public static void Main(String[] args) {
        List<O> L1 = new List<O>{
            new O {K = 1, V = "abcd"},
            new O {K = 2, V = "efgh"}
        };
        List<O> L2 = new List<O>{
            new O {K = 1, V = "abcd"}
        };
        List<O> L3 = new List<O>{
            new O {K = 1, V = "abcd"},
            new O {K = 3, V = "ijkl"}
        };
        List<O> L4 = new List<O>{
            new O {K = 2, V = "efgh"},
            new O {K = 1, V = "abcd"}

        };

        Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray()).Count());
        Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L2.Select(x => x.K).ToArray()).Count());
        Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L3.Select(x => x.K).ToArray()).Count());
        Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L4.Select(x => x.K).ToArray()).Count());

    }
} 

public class O {
    public int K { get; set; }
    public String V { get; set; }
}
于 2013-01-09T14:07:23.203 に答える
1

まさにそれが言うこと。
クラス Employee に IComparable を実装
する Equals をオーバーライドする必要もあります。GetHashCode
への呼び出しが多数になる可能性があるため、保存して変更のみを計算します。
テスト済み

IComparable インターフェイス

public MainWindow()
{
    InitializeComponent();
    List<Person> PLa = new List<Person>();
    List<Person> PLb = new List<Person>();

    PLa.Add(new Person { Age = 3, Name = "Jim"});
    PLa.Add(new Person { Age = 2, Name = "Jimmmy" });
    PLa.Add(new Person { Age = 1, Name = "Jim" });

    PLb.Add(new Person { Age = 1, Name = "Jim" });
    PLb.Add(new Person { Age = 3, Name = "Jim" });
    PLb.Add(new Person { Age = 2, Name = "Jimmmy" });

    System.Diagnostics.Debug.WriteLine(ListSameIgnoreOrder(PLa, PLb));

}

public bool ListSameIgnoreOrder(List<Person> PLa, List<Person> PLb)
{
    if (PLa.Count != PLb.Count) return false;
    //PLa.Sort();
    //PLb.Sort();
    return Enumerable.SequenceEqual(PLa.OrderBy(s => s), PLb.OrderBy(s => s));
    //for (int i = 0; i < PLa.Count; i++)
    //{
    //    System.Diagnostics.Debug.WriteLine(
    //        PLa[i].Age.ToString() + " " + PLb[i].Age.ToString() + " " +
    //        PLa[i].Name + " " + PLb[i].Name);
    //    if (!PLa[i].Equals(PLb[i])) return false;
    //}
    //return true;
}

public class Person : object, IComparable
{
    private int age = 0;
    private string name = string.Empty;
    private int hash;

    public int Age
    {
        get { return age; }
        set 
        {
            if (age == value) return;
            age = value;
            CalcHash();
        }
    }
    public string Name
    {
        get { return name; }
        set 
        { 
            if (name == value) return;
            name = value;
            CalcHash();
        }
    }

    public override bool Equals(Object obj)
    {
        //Check for null and compare run-time types.
        if (obj == null || !(obj is Person)) return false;
        Person f = (Person)obj;
        if (f.Age != this.Age) return false;
        return (string.Compare(f.name, this.name) == 0);
    }

    private void CalcHash()
    {
        hash = Age.GetHashCode() ^
            (Name ?? String.Empty).GetHashCode();
    }

    public override int GetHashCode()
    {
        return hash;
        //return age ^ name.GetHashCode();
    }

    public int CompareTo(object obj)
    {
        if (obj == null) return 1;

        Person otherPerson = obj as Person;
        if (otherPerson != null)
        {
            if (otherPerson.Age > this.Age) return -1;
            if (otherPerson.Age < this.Age) return 1;
            // compare all properties like above
            return string.Compare(otherPerson.name, this.name);
        }
        else
            throw new ArgumentException("Object is not a Person");
    }
    public Person() { CalcHash(); }
}
于 2013-01-09T14:08:32.777 に答える