Ответ 1
Enumerable.SequenceEqual<TSource>
Есть много способов сделать это, но я чувствую, что упустил какую-то функцию или что-то в этом роде.
Очевидно, что List == List
будет использовать Object.Equals()
и вернуть false
.
Если каждый элемент списка равен и присутствует в том же месте в противоположном списке, я бы счел их равными. Я использую типы значений, но правильно реализованный объект Data должен работать одинаково (я не ищу мелкий скопированный список, только то, что значение каждого объекта внутри одинаково).
Я пробовал поиск и есть похожие вопросы, но мой вопрос - это равенство каждого элемента в точном порядке.
Enumerable.SequenceEqual<TSource>
Реализация зла
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();
}
}
Это имеет несколько особенностей:
Сравнение выполняется снизу вверх. В типичных случаях использования существует большая вероятность того, что коллекции будут отличаться в конце.
А IEqualityComparer<T>
можно передать, чтобы основывать сравнение для элементов в коллекции.
Используйте linq SequenceEqual
, чтобы проверить равенство последовательности, потому что метод Equals проверяет ссылочное равенство.
bool isEqual = list1.SequenceEqual(list2);
Метод SequenceEqual()
принимает вторую последовательность я Enumerable<T>
в качестве параметра и выполняет сравнение по элементам с целевой (первой) последовательностью. Если две последовательности содержат одинаковое число элементов, и каждый элемент в первой последовательности равен соответствующему элементу во второй последовательности (с использованием сравнения по умолчанию), тогда SequenceEqual()
returns true
. В противном случае возвращается false
.
Или, если вам не нужен порядок элементов, используйте метод Enumerable.All
:
var isEqual = list1.All(list2.Contains);
Вторая версия также требует другой проверки для Count, потому что она вернет true, даже если list2
содержит больше элементов, чем list1
.