Самый быстрый способ проверить, равны ли два списка <T>
У меня есть два списка
ListA<Emp>
и ListB<Emp>
оба имеют 1000 записей.
Emp
является объектом класса Employee. Ниже представлен мой класс 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)
Как я могу это реализовать? Также будет лучше, если вы, ребята, можете предоставить мне самый быстрый способ сделать это, потому что количество объектов в списке может вырасти до 10 миллионов.
Спасибо за вашу помощь!
EDIT: Каждый сотрудник должен быть в обоих списках, порядок не имеет значения. Но если ListA содержит один и тот же объект-работник 5 раз (это означает несколько дубликатов записей), а ListB содержит объект employee 4 раза, то ListA и ListB не равны.
Ответы
Ответ 1
Наилучшей сложностью является 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;
}
Ответ 2
Вы можете использовать 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));
Ответ 3
Если числа в списке будут расти огромными (10M), вам, вероятно, придется рассмотреть возможность распараллеливания поиска, чтобы получить приемлемое время запроса.
Рассмотрите возможность использования PLINQ.
Еще одна ясность в отношении того, что вы подразумеваете под "равным", была бы хорошей. Насколько сложна проверка эквивалентности? Вы проверяете, что объекты одинаковы или что значения объектов одинаковы?
Еще одно соображение: если количество элементов станет большим, не могли бы вы рассмотреть возможность переноса этой проверки с .NET в вашу базу данных - возможно, в качестве хранимой процедуры? Вы можете обнаружить, что он работает там более эффективно.
Ответ 4
уменьшить список до скалярного типа: int, string,....
L1.Select(x => x.K).ToArray()
используйте метод except
L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray())
Если счетчик результирующего набора равен 0, то List равен
L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray()).Count()
Все вместе
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; }
}
Ответ 5
То, что он говорит.
Внедрение IComparable в класс Сотрудник
Также необходимо переопределить Equals
Из-за потенциально большого количества вызовов GetHashCode сохраняет его и только рассчитывает на изменения.
Протестировано
IComparable Interface
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(); }
}
Ответ 6
Это работает.
public bool EqualList(Dictionary<int, string> a, Dictionary<int, string> b)
{
if (a.Count == b.Count)
{
bool rs = false;
foreach (var i in a)
{
if (b.ContainsKey(i.Key))
{
rs = true;
}
else
{
rs = false;
break;
}
}
return rs;
}
else
{
return false;
}
Использование:
if(EqualList(List<A>.ToDictionary(k => k.Key, k => k.Value), List<B>.ToDictionary(k => k.Key, k => k.Value)){
}else{
}