我认为可能没有,至少目前没有。原因是集合的相等性可能是用户定义的行为。
集合中的元素不应该按特定顺序排列,尽管它们自然上有一种排序方式,但比较算法不应该依赖于此。假设您有两个集合:
{1, 2, 3, 4}
{4, 3, 2, 1}
它们是否相等?你必须知道,但我不知道你的观点是什么。
集合在概念上默认是无序的,直到算法提供排序规则。同样的事情,SQL服务器会提醒你需要提供排序规则,当你尝试进行分页时:
又是另外两个集合:
{1, 2, 3, 4}
{1, 1, 1, 2, 2, 3, 4}
再次确认,它们是否相等?你告诉我...
集合中元素的可重复性在不同的场景中发挥着作用,有些集合(如Dictionary<TKey, TValue>
)甚至不允许重复元素。
我认为这些类型的相等性是应用程序定义的,因此框架没有提供所有可能的实现。
总的来说,在一般情况下,Enumerable.SequenceEqual
已经足够好了,但在以下情况下它会返回false:
var a = new Dictionary<String, int> { { "2", 2 }, { "1", 1 }, };
var b = new Dictionary<String, int> { { "1", 1 }, { "2", 2 }, };
Debug.Print("{0}", a.SequenceEqual(b)); // false
我阅读了一些类似这样的问题的答案(你可以谷歌搜索它们),通常我会使用以下内容:
public static class CollectionExtensions {
public static bool Represents<T>(this IEnumerable<T> first, IEnumerable<T> second) {
if(object.ReferenceEquals(first, second)) {
return true;
}
if(first is IOrderedEnumerable<T> && second is IOrderedEnumerable<T>) {
return Enumerable.SequenceEqual(first, second);
}
if(first is ICollection<T> && second is ICollection<T>) {
if(first.Count()!=second.Count()) {
return false;
}
}
first=first.OrderBy(x => x.GetHashCode());
second=second.OrderBy(x => x.GetHashCode());
return CollectionExtensions.Represents(first, second);
}
}
这意味着一个集合可以代表另一个集合的元素,包括重复次数,而无需考虑原始顺序。一些实现的注释:
GetHashCode()
只用于排序,而不是用于相等性;我认为在这种情况下足够了
Count()
不会真正枚举集合,并直接进入 ICollection<T>.Count
的属性实现
如果引用相等,那就是 Boris
public bool CompareStringLists(List<string> list1, List<string> list2)
{
if (list1.Count != list2.Count) return false;
foreach(string item in list1)
{
if (!list2.Contains(item)) return false;
}
return true;
}
http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx
IComparer<T>
、重写 object.Equals
、IEquatable<T>
、IComparable<T>
... - Stefan Steinegger我已经编写了自己的比较方法。它返回共同的、缺失的和额外的值。
private static void Compare<T>(IEnumerable<T> actual, IEnumerable<T> expected, out IList<T> common, out IList<T> missing, out IList<T> extra) {
common = new List<T>();
missing = new List<T>();
extra = new List<T>();
var expected_ = new LinkedList<T>( expected );
foreach (var item in actual) {
if (expected_.Remove( item )) {
common.Add( item );
} else {
extra.Add( item );
}
}
foreach (var item in expected_) {
missing.Add( item );
}
}
要比较两个Dictionary<K,V>
对象,我们可以假设每个值的键都是唯一的,因此如果两组键相等,则两个字典的内容也相等。
Dictionary<K, V> dictionaryA, dictionaryB;
bool areDictionaryContentsEqual = new HashSet<K>(dictionaryA.Keys).SetEquals(dictionaryB.Keys);
要比较两个 ICollection<T>
对象,我们需要检查以下内容:
T
值在第二个集合中出现的次数是否相等。public static bool AreCollectionContentsEqual<T>(ICollection<T> collectionA, ICollection<T> collectionB)
where T : notnull
{
if (collectionA.Count != collectionB.Count)
{
return false;
}
Dictionary<T, int> countByValueDictionary = new(collectionA.Count);
foreach(T item in collectionA)
{
countByValueDictionary[item] = countByValueDictionary.TryGetValue(item, out int count)
? count + 1
: 1;
}
foreach (T item in collectionB)
{
if (!countByValueDictionary.TryGetValue(item, out int count) || count < 1)
{
return false;
}
countByValueDictionary[item] = count - 1;
}
return true;
}
这些解决方案应该是最优的,因为它们的时间和内存复杂度都是O(n)
,而使用排序/排序的解决方案的时间和内存复杂度大于O(n)
。
Enumerable.SequenceEqual
和ISet.SetEquals
提供了这个功能的版本。如果你想要无序并且处理有重复项的集合,你需要自己编写代码。请查看此文章中提出的实现建议。 - ChaseMedallionCollectionAssert.AreEquivalent
。 - alelom