190

我想在我的 Equals 方法中比较几个集合的内容。我有一个字典和一个 IList。有没有内置的方法可以做到这一点?

编辑:我想比较两个字典和两个 IList,所以我认为相等的含义很清楚 - 如果两个字典包含映射到相同值的相同键,那么它们是相等的。

4

15 回答 15

197

Enumerable.SequenceEqual

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

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

于 2008-09-04T11:22:19.127 回答
45

正如其他人所建议和指出的那样,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))
于 2011-01-11T08:43:57.387 回答
16

除了提到的SequenceEqual之外,其中

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

(可能是默认比较器,即覆盖Equals()

值得一提的是,在 .Net4 中,对象上有SetEqualsISet,它

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

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

于 2012-02-19T10:58:05.927 回答
6

看一下Enumerable.SequenceEqual 方法

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);
于 2008-09-04T11:23:11.010 回答
5

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

 CollectionAssert.AreEquivalent

这几乎可以满足您的需求。

于 2012-09-18T03:45:49.857 回答
4

我不知道 Enumerable.SequenceEqual 方法(你每天都会学到一些东西......),但我会建议使用扩展方法;像这样的东西:

    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 秒时间阅读了有关 SequenceEqual 的内容后,看起来微软已经构建了我为您描述的功能。

于 2008-09-04T11:31:09.400 回答
4

.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
于 2010-04-29T20:26:10.060 回答
1

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

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

第一次比较是必要的,因为dict2可以包含所有的键 fromdict1和更多。

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

于 2016-09-27T08:43:16.317 回答
1

这个例子怎么样:

 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

于 2017-04-11T18:19:03.027 回答
1

对于有序集合(列表、数组)使用SequenceEqual

用于 HashSetSetEquals

对于字典,您可以执行以下操作:

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>

于 2018-08-17T22:30:31.297 回答
0

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

看看这个:

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

于 2008-09-04T11:20:01.377 回答
0
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;
}
于 2016-03-12T21:51:40.977 回答
0

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

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

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

他们是平等的还是不平等的?你一定知道,但我不知道你的观点是什么。

默认情况下,集合在概念上是无序的,直到算法提供排序规则。SQL Server 会引起您注意的同样是当您尝试进行分页时,它要求您提供排序规则:

https://docs.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

  • 如果引用相同,那就是鲍里斯

于 2018-06-09T13:57:26.450 回答
0

我已经做了自己的比较方法。它返回常见的、缺失的和额外的值。

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 );
    }
}
于 2020-08-18T20:28:33.523 回答
-2

不,集合框架没有任何平等的概念。如果您考虑一下,就无法比较不主观的收藏。例如,将您的 IList 与您的 Dictionary 进行比较,如果所有键都在 IList 中,所有值都在 IList 中,或者两者都在 IList 中,它们是否相等?在不知道它们的用途的情况下,没有明显的方法可以比较这两个集合,因此通用的 equals 方法没有意义。

于 2008-09-04T11:33:32.657 回答