.net - 比较两个集合是否相等,无论其中项目的顺序如何

标签 .net collections comparison equality

我想比较两个集合(在 C# 中),但我不确定有效实现此目的的最佳方法。

我读过关于 Enumerable.SequenceEqual 的其他帖子,但这并不完全是我正在寻找的。

就我而言,如果两个集合都包含相同的项目(无论顺序如何),则它们是相等的。

示例:

collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};

collection1 == collection2; // true

我通常做的是循环遍历一个集合的每个项目,看看它是否存在于另一个集合中,然后循环遍历另一个集合的每个项目,看看它是否存在于第一个集合中。 (我首先比较长度)。

if (collection1.Count != collection2.Count)
    return false; // the collections are not equal

foreach (Item item in collection1)
{
    if (!collection2.Contains(item))
        return false; // the collections are not equal
}

foreach (Item item in collection2)
{
    if (!collection1.Contains(item))
        return false; // the collections are not equal
}

return true; // the collections are equal

但是,这并不完全正确,而且它可能不是比较两个集合是否相等的最有效方法。

我能想到的一个错误的例子是:

collection1 = {1, 2, 3, 3, 4}
collection2 = {1, 2, 2, 3, 4}

这与我的实现相同。我是否应该只计算每个项目被发现的次数并确保两个集合中的计数相等?

<小时/>

这些示例是某种 C# 语言(我们称之为伪 C#),但是用您希望的任何语言给出答案都没关系。

注意:为了简单起见,我在示例中使用了整数,但我也希望能够使用引用类型对象(它们作为键的行为不正确,因为只有对象的引用是比较,而不是内容)。

最佳答案

事实证明,微软已经在其测试框架中涵盖了这一点:CollectionAssert.AreEquivalent

Remarks

Two collections are equivalent if they have the same elements in the same quantity, but in any order. Elements are equal if their values are equal, not if they refer to the same object.

使用反射器,我修改了 AreEquivalent() 后面的代码来创建相应的相等比较器。它比现有答案更完整,因为它考虑了空值,实现了 IEqualityComparer 并具有一些效率和边缘情况检查。另外,它是微软:)

public class MultiSetComparer<T> : IEqualityComparer<IEnumerable<T>>
{
    private readonly IEqualityComparer<T> m_comparer;
    public MultiSetComparer(IEqualityComparer<T> comparer = null)
    {
        m_comparer = comparer ?? EqualityComparer<T>.Default;
    }

    public bool Equals(IEnumerable<T> first, IEnumerable<T> second)
    {
        if (first == null)
            return second == null;

        if (second == null)
            return false;

        if (ReferenceEquals(first, second))
            return true;

        if (first is ICollection<T> firstCollection && second is ICollection<T> secondCollection)
        {
            if (firstCollection.Count != secondCollection.Count)
                return false;

            if (firstCollection.Count == 0)
                return true;
        }

        return !HaveMismatchedElement(first, second);
    }

    private bool HaveMismatchedElement(IEnumerable<T> first, IEnumerable<T> second)
    {
        int firstNullCount;
        int secondNullCount;

        var firstElementCounts = GetElementCounts(first, out firstNullCount);
        var secondElementCounts = GetElementCounts(second, out secondNullCount);

        if (firstNullCount != secondNullCount || firstElementCounts.Count != secondElementCounts.Count)
            return true;

        foreach (var kvp in firstElementCounts)
        {
            var firstElementCount = kvp.Value;
            int secondElementCount;
            secondElementCounts.TryGetValue(kvp.Key, out secondElementCount);

            if (firstElementCount != secondElementCount)
                return true;
        }

        return false;
    }

    private Dictionary<T, int> GetElementCounts(IEnumerable<T> enumerable, out int nullCount)
    {
        var dictionary = new Dictionary<T, int>(m_comparer);
        nullCount = 0;

        foreach (T element in enumerable)
        {
            if (element == null)
            {
                nullCount++;
            }
            else
            {
                int num;
                dictionary.TryGetValue(element, out num);
                num++;
                dictionary[element] = num;
            }
        }

        return dictionary;
    }

    public int GetHashCode(IEnumerable<T> enumerable)
    {
        if (enumerable == null) throw new 
            ArgumentNullException(nameof(enumerable));

        int hash = 17;

        foreach (T val in enumerable)
            hash ^= (val == null ? 42 : m_comparer.GetHashCode(val));

        return hash;
    }
}

使用示例:

var set = new HashSet<IEnumerable<int>>(new[] {new[]{1,2,3}}, new MultiSetComparer<int>());
Console.WriteLine(set.Contains(new [] {3,2,1})); //true
Console.WriteLine(set.Contains(new [] {1, 2, 3, 3})); //false

或者,如果您只想直接比较两个集合:

var comp = new MultiSetComparer<string>();
Console.WriteLine(comp.Equals(new[] {"a","b","c"}, new[] {"a","c","b"})); //true
Console.WriteLine(comp.Equals(new[] {"a","b","c"}, new[] {"a","b"})); //false

最后,您可以使用您选择的相等比较器:

var strcomp = new MultiSetComparer<string>(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(strcomp.Equals(new[] {"a", "b"}, new []{"B", "A"})); //true

关于.net - 比较两个集合是否相等,无论其中项目的顺序如何,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50098/

相关文章:

c# - 什么相当于 C# 中的 X509EncodedKeySpec

javascript - 无法使用 React 显示 mongo 集合

java - Java 中的大数比较

c# - 带有 XML 文件的 Entity Framework

.NET C# WinForms 贸易公司考试

c# - RunWorkerAsync 完成时关闭BackgroundWorker

Python比较运算符重载

java - 将 POJO 转换为 Java 8 中的树结构以在层次结构中表示数据

c# - GetEnumerator 的重载

java - 无效的 if 语句