Каков наилучший способ проверить два списка списков на равенство в C#
есть много способов сделать это, но я чувствую, что я пропустил функцию или что-то.
очевидно List == List
использовать Object.Equals()
и return false
.
Если каждый элемент списка равен и присутствует в одном и том же месте в противоположном списке, я бы считал их равными. Я использую типы значений, но правильно реализованный объект данных должен работать таким же образом (i.e я не ищу мелкий скопированный список, только то, что стоимостью каждого объекта внутри то же самое).
Я пробовал поиск, и есть похожие вопросы, но мой вопрос-это равенство каждого элемента в точном порядке.
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, и у этого есть небольшое преимущество.
теперь вопрос: стоит ли этот крошечный, почти измеримый прирост производительности добавлять код в базу кода и поддерживать его? Я очень сомневаюсь в этом; o)
я постучал быстрый метод расширения:
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();
}
}
это имеет несколько особенностей:
сравнение выполняется снизу вверх. Существует большая вероятность для коллекций, отличающихся в конце в типичных случаях использования.
An
IEqualityComparer<T>
может быть принят в основу сравнения предметы в коллекции.
использовать в LINQ SequenceEqual
чтобы проверить равенство последовательности, потому что метод Equals проверяет равенство ссылок.
bool isEqual = list1.SequenceEqual(list2);
на SequenceEqual()
метод занимает секунду IEnumerable<T>
последовательность в качестве параметра и выполняет сравнение, элемент-за-элементом, с целевым (первой) последовательности. Если две последовательности содержат тот же номер элементов, и каждый элемент первой последовательности равен соответствующему элементу второго последовательность (используя по умолчанию компаратор проверки на равенство) то SequenceEqual()
returns true
. В противном случае, false
возвращается.
или если вы не заботитесь о порядке использования элементов Enumerable.All
способ:
var isEqual = list1.All(list2.Contains);
вторая версия также требует другой проверки для Count, потому что она вернет true, даже если list2
содержится больше элементов, чем list1
.