Каков наилучший способ проверить два списка List<T> на равенство в C#
Есть много способов сделать это, но я чувствую, что пропустил функцию или что-то в этом роде.
очевидно List == List
буду использовать Object.Equals()
и вернуться false
,
Если все элементы списка равны и присутствуют в одном и том же месте в противоположном списке, я бы посчитал их равными. Я использую типы значений, но правильно реализованный объект данных должен работать таким же образом (т.е. я не ищу мелкий скопированный список, только то, что значение каждого объекта внутри одинаково).
Я пробовал искать, и есть похожие вопросы, но мой вопрос - равенство каждого элемента в точном порядке.
6 ответов
Злая реализация
if (List1.Count == List2.Count)
{
for(int i = 0; i < List1.Count; i++)
{
if(List1[i] != List2[i])
{
return false;
}
}
return true;
}
return false;
Я собрал этот вариант:
private bool AreEqual<T>(List<T> x, List<T> y)
{
// same list or both are null
if (x == y)
{
return true;
}
// one is null (but not the other)
if (x== null || y == null)
{
return false;
}
// count differs; they are not equal
if (x.Count != y.Count)
{
return false;
}
for (int i = 0; i < x.Count; i++)
{
if (!x[i].Equals(y[i]))
{
return false;
}
}
return true;
}
Во мне тоже выполз ботаник, поэтому я провел тест производительности на SequenceEquals, и у этого есть небольшое преимущество.
Теперь вопрос, чтобы задать; стоит ли этот крошечный, почти измеримый прирост производительности, добавляя код в базу кода и поддерживая его? Я очень в этом сомневаюсь; о)
Я выбил быстрый метод расширения:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static bool Matches<T>(this List<T> list1, List<T> list2)
{
if (list1.Count != list2.Count) return false;
for (var i = 0; i < list1.Count; i++)
{
if (list1[i] != list2[i]) return false;
}
return true;
}
}
}
Можно написать общее назначение IEqualityComparer<T>
для последовательностей. Простой:
public class SequenceEqualityComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<T> obj)
{
return unchecked(obj.Aggregate(397, (x, y) => x * 31 + y.GetHashCode()));
}
}
Более конкретная версия: которая должна быть лучше.
public class SequenceEqualityComparer<T> : EqualityComparer<IEnumerable<T>>,
IEquatable<SequenceEqualityComparer<T>>
{
readonly IEqualityComparer<T> comparer;
public SequenceEqualityComparer(IEqualityComparer<T> comparer = null)
{
this.comparer = comparer ?? EqualityComparer<T>.Default;
}
public override bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
// safer to use ReferenceEquals as == could be overridden
if (ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
var xICollection = x as ICollection<T>;
if (xICollection != null)
{
var yICollection = y as ICollection<T>;
if (yICollection != null)
{
if (xICollection.Count != yICollection.Count)
return false;
var xIList = x as IList<T>;
if (xIList != null)
{
var yIList = y as IList<T>;
if (yIList != null)
{
// optimization - loops from bottom
for (int i = xIList.Count - 1; i >= 0; i--)
if (!comparer.Equals(xIList[i], yIList[i]))
return false;
return true;
}
}
}
}
return x.SequenceEqual(y, comparer);
}
public override int GetHashCode(IEnumerable<T> sequence)
{
unchecked
{
int hash = 397;
foreach (var item in sequence)
hash = hash * 31 + comparer.GetHashCode(item);
return hash;
}
}
public bool Equals(SequenceEqualityComparer<T> other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return this.comparer.Equals(other.comparer);
}
public override bool Equals(object obj)
{
return Equals(obj as SequenceEqualityComparer<T>);
}
public override int GetHashCode()
{
return comparer.GetHashCode();
}
}
Это имеет несколько особенностей:
Сравнение делается снизу вверх. Существует большая вероятность того, что коллекции отличаются в конце в типичных сценариях использования.
IEqualityComparer<T>
может быть передано в базу сравнения предметов в коллекции.
Используйте linq SequenceEqual
проверить на равенство последовательности, потому что метод Equals проверяет на равенство ссылок.
bool isEqual = list1.SequenceEqual(list2);
SequenceEqual()
метод занимает секунду я Enumerable<T>
sequence в качестве параметра и выполняет поэлементное сравнение с целевой (первой) последовательностью. Если две последовательности содержат одинаковое количество элементов, и каждый элемент в первой последовательности равен соответствующему элементу во второй последовательности (с использованием средства сравнения равенства по умолчанию), тогда SequenceEqual()
returns true
, Иначе, false
возвращается
Или, если вы не заботитесь о порядке использования элементов Enumerable.All
метод:
var isEqual = list1.All(list2.Contains);
Вторая версия также требует еще одну проверку для Count, потому что он вернул бы true, даже если list2
содержит больше элементов, чем list1
,