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

197
我希望在我的Equals方法中比较几个集合的内容。我有一个字典和一个列表。是否有内置方法来做到这一点? 编辑: 我想比较两个字典和两个列表,所以我认为相等的含义很清楚——如果两个字典包含相同的键映射到相同的值,则它们相等。

Enumerable.SequenceEqualISet.SetEquals提供了这个功能的版本。如果你想要无序并且处理有重复项的集合,你需要自己编写代码。请查看此文章中提出的实现建议。 - ChaseMedallion
如下评论所述,对于99%的情况,您可以依赖NUnit/MSTest方法CollectionAssert.AreEquivalent - alelom
@alexlomba87 那个函数值得一提,但是依赖于测试程序集来进行生产代码是否有些不妥呢? - matt
15个回答

0

我认为可能没有,至少目前没有。原因是集合的相等性可能是用户定义的行为。

集合中的元素不应该按特定顺序排列,尽管它们自然上有一种排序方式,但比较算法不应该依赖于此。假设您有两个集合:

{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


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

0

3
难道框架不已经提供了多种选项来告诉它如何比较元素吗?IComparer<T>、重写 object.EqualsIEquatable<T>IComparable<T> ... - Stefan Steinegger

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

-1

比较字典内容:

要比较两个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)


你的字典比较并没有检查值是否相等,仅仅是检查它们是否有相同的键。显然,这是不足以检查“内容是否相等”的。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接