是否有比较集合的内置方法?

我想比较 Equals 方法中两个集合的内容。我有一本字典和一个 IList。是否有一个内置的方法来做到这一点?

< p > 编辑: 我想比较两个 Dictionaries 和两个 ILists,所以我认为相等的含义很清楚——如果两个 dictionary 包含映射到相同值的相同键,那么它们就是相等的。译注:
96292 次浏览

不,因为框架不知道如何比较列表的内容。

看看这个:

http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx

译自: 美国《科学》杂志网站(http://msdn.microsoft.com/en-us/library/bb342073.aspx)

通过使用指定的 IEqualityComparer (T)比较两个序列的元素来确定它们是否相等。

您不能直接比较列表和字典,但是您可以将字典中的值列表与列表进行比较

看一下 可枚举的,序列相等的方法

var dictionary = new Dictionary<int, string>() \{\{1, "a"}, {2, "b"}};
var intList = new List<int> {1, 2};
var stringList = new List<string> {"a", "b"};
var test1 = dictionary.Keys.SequenceEqual(intList);
var test2 = dictionary.Values.SequenceEqual(stringList);

我不知道 Enumable。Sequenceequals 方法(你每天都会学到一些东西... ...) ,但是我建议使用扩展方法,比如:

    public static bool IsEqual(this List<int> InternalList, List<int> ExternalList)
{
if (InternalList.Count != ExternalList.Count)
{
return false;
}
else
{
for (int i = 0; i < InternalList.Count; i++)
{
if (InternalList[i] != ExternalList[i])
return false;
}
}


return true;


}

有趣的是,在花了2秒钟阅读 Sequenceequals 之后,看起来微软已经构建了我为您描述的函数。

NET 缺乏任何强大的工具来比较集合。我开发了一个简单的解决方案,你可以在下面的链接中找到:

http://robertbouillon.com/2010/04/29/comparing-collections-in-net/

这将执行相等比较,无论顺序如何:

var list1 = new[] { "Bill", "Bob", "Sally" };
var list2 = new[] { "Bob", "Bill", "Sally" };
bool isequal = list1.Compare(list2).IsSame;

这将检查项目是否添加/删除:

var list1 = new[] { "Billy", "Bob" };
var list2 = new[] { "Bob", "Sally" };
var diff = list1.Compare(list2);
var onlyinlist1 = diff.Removed; //Billy
var onlyinlist2 = diff.Added;   //Sally
var inbothlists = diff.Equal;   //Bob

这将看到字典中哪些条目发生了变化:

var original = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" } };
var changed = new Dictionary<int, string>() { { 1, "aaa" }, { 2, "b" } };
var diff = original.Compare(changed, (x, y) => x.Value == y.Value, (x, y) => x.Value == y.Value);
foreach (var item in diff.Different)
Console.Write("{0} changed to {1}", item.Key.Value, item.Value.Value);
//Will output: a changed to aaa

正如其他人所建议和指出的,SequenceEqual是顺序敏感的。要解决这个问题,可以按键对字典进行排序(这是唯一的,因此排序总是稳定的) ,然后使用 SequenceEqual。下面的表达式检查两个字典是否相等,而不管它们的内部顺序如何:

dictionary1.OrderBy(kvp => kvp.Key).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))

正如 Jeppe Stig Nielsen 指出的那样,有些物体的 IComparer<T>与它们的 IEqualityComparer<T>不兼容,从而产生了不正确的结果。对这样的对象使用键时,必须为这些键指定正确的 IComparer<T>。例如,对于字符串键(它展示了这个问题) ,您必须执行以下操作才能得到正确的结果:

dictionary1.OrderBy(kvp => kvp.Key, StringComparer.Ordinal).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))

除了提到的 顺序相等,其中

如果两个列表的长度相等,并且它们相对应,则 < p > 为 true 元素根据比较器

进行比较

(它可能是默认的比较器,即重写的 Equals())

值得一提的是,在.Net4中,ISet对象上有 SetEquals, 其中

忽略元素的顺序和任何重复的元素。

因此,如果您想要一个对象列表,但是它们不需要按照特定的顺序排列,那么考虑使用 ISet(如 HashSet)可能是正确的选择。

这不是直接回答您的问题,但是 MS 的 TestTools 和 NUnit 都提供了

 CollectionAssert.AreEquivalent

你想怎么做就怎么做。

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;
}

要比较集合,还可以使用 LINQ。Enumerable.Intersect返回所有相等的对。你可以这样比较两本字典:

(dict1.Count == dict2.Count) && dict1.Intersect(dict2).Count() == dict1.Count

需要进行第一个比较,因为 dict2可以包含来自 dict1的所有键以及更多键。

您还可以使用使用 Enumerable.ExceptEnumerable.Union的变体来导致类似的结果。但是可以用来确定集合之间的确切差异。

这个例子怎么样:

 static void Main()
{
// Create a dictionary and add several elements to it.
var dict = new Dictionary<string, int>();
dict.Add("cat", 2);
dict.Add("dog", 3);
dict.Add("x", 4);


// Create another dictionary.
var dict2 = new Dictionary<string, int>();
dict2.Add("cat", 2);
dict2.Add("dog", 3);
dict2.Add("x", 4);


// Test for equality.
bool equal = false;
if (dict.Count == dict2.Count) // Require equal count.
{
equal = true;
foreach (var pair in dict)
{
int value;
if (dict2.TryGetValue(pair.Key, out value))
{
// Require value be equal.
if (value != pair.Value)
{
equal = false;
break;
}
}
else
{
// Require key be present.
equal = false;
break;
}
}
}
Console.WriteLine(equal);
}

提供者: https://www.dotnetperls.com/dictionary-equals

没有,现在没有,也可能没有,至少我是这么认为的。其背后的原因是集合相等性可能是用户定义的行为。

集合中的元素不应该按照特定的顺序排列,尽管它们确实有一个自然的顺序,这不是比较算法应该依赖的顺序。假设你有两个系列:

{1, 2, 3, 4}
{4, 3, 2, 1}

他们是否平等? 你必须知道,但我不知道你的观点。

默认情况下,集合在概念上是无序的,直到算法提供排序规则。SQL 服务器将提醒您的同样的事情是,当您尝试进行分页时,它需要您提供排序规则:

https://learn.microsoft.com/en-US/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017

还有两个收藏品:

{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 了

对于有序的集合(List,Array) ,使用 SequenceEqual

对于 HashSet 使用 SetEquals

你可以做的字典:

namespace System.Collections.Generic {
public static class ExtensionMethods {
public static bool DictionaryEquals<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> d1, IReadOnlyDictionary<TKey, TValue> d2) {
if (object.ReferenceEquals(d1, d2)) return true;
if (d2 is null || d1.Count != d2.Count) return false;
foreach (var (d1key, d1value) in d1) {
if (!d2.TryGetValue(d1key, out TValue d2value)) return false;
if (!d1value.Equals(d2value)) return false;
}
return true;
}
}
}

(一个更优化的解决方案将使用排序,但这将需要 IComparable<TValue>)

我自己创建了一个比较方法,它返回公共值、缺失值和额外值。

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>对象,我们需要检查:

  1. 如果它们的长度相同。
  2. 如果在第一个集合中出现的每个 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)