What is the best way to determine if two List <T> objects contain the same set of values, even if they are not in the same order?

I have two objects List<T>(where Tis the same type for both objects) and I need to determine if they contain the same set of values, even if the values ​​are not in the same order.

Do objects have any built-in mechanisms to accomplish this, or do I need to write my own algorithm?

Or maybe I should use a different type of collection, not List<T>?

If I wrote my own algorithm, it would probably consist of the following steps: I will try to optimize this in the final version if I go this route:

  • Do both collections have the same number of values? If not return false.
  • Count how many times each value appears in each collection, returns false if the counts are not equal.
  • If I get to the end of both collections without any disparity in count values, return true.

I know that there are some caveats to this, for example, the fact that T must be comparable - I use the default comparison at the moment (for example .Equals()) with the corresponding restrictions defined for the typical type.

0
source share
3 answers

Based on the available information, I suspect that the most effective duplicate-supporting solution is

  • . , false. ,
  • . , .
  • , false, . true, , .

, , ( ).

+1

, SetEquals . HashSet , , , :

public static bool SetEquals<T>(this IEnumerable<T> first, IEnumerable<T> second,
    IEqualityComparer<T> comparer = null)
{
    return new HashSet<T>(second, comparer ?? EqualityComparer<T>.Default)
        .SetEquals(first);
}

, , , , , , , . , , . , :

public static bool BagEquals<T>(
    this IEnumerable<T> first,
    IEnumerable<T> second)
{
    Func<IEnumerable<T>, IEnumerable<KeyValuePair<T, int>>> groupItems =
        sequence => sequence.GroupBy(item => item,
            (key, items) => new KeyValuePair<T, int>(key, items.Count()));
    return groupItems(first)
        .SetEquals(groupItems(second));
}
+1

Here is a reimplementation CollectionAssert.AreEquivalent(the link code was decompiled with DotPeek), however, instead of throwing an exception, it returns bool.

public class CollectionMethods
{
    public static bool AreEquivalent(ICollection expected, ICollection actual)
    {
        //We can do a few quick tests we can do to get a easy true or easy false.
        //Is one collection null and one not?
        if (Object.ReferenceEquals(expected, null) != Object.ReferenceEquals(actual, null))
            return false;

        //Do they both point at the same object?
        if (Object.ReferenceEquals(expected, actual))
            return true;

        //Do they have diffrent counts?
        if (expected.Count != actual.Count)
            return false;

        //Do we have two empty collections?
        if (expected.Count == 0)
            return true;


        //Ran out of easy tests, now have to do the slow work.
        int nullCount1;
        Dictionary<object, int> elementCounts1 = CollectionMethods.GetElementCounts(expected, out nullCount1);
        int nullCount2;
        Dictionary<object, int> elementCounts2 = CollectionMethods.GetElementCounts(actual, out nullCount2);

        //One last quick check, do the two collections have the same number of null elements?
        if (nullCount2 != nullCount1)
        {
            return false;
        }

        //Check for each element and see if we see them the same number of times in both collections.
        foreach (KeyValuePair<object,int> kvp in elementCounts1)
        {
            int expectedCount = kvp.Value;
            int actualCount;
            elementCounts2.TryGetValue(key, out actualCount);
            if (expectedCount != actualCount)
            {
                return false;
            }
        }
        return true;
    }

    private static Dictionary<object, int> GetElementCounts(ICollection collection, out int nullCount)
    {
        Dictionary<object, int> dictionary = new Dictionary<object, int>();
        nullCount = 0;
        foreach (object key in (IEnumerable)collection)
        {
            if (key == null)
            {
                ++nullCount;
            }
            else
            {
                int num;
                dictionary.TryGetValue(key, out num);
                ++num;
                dictionary[key] = num;
            }
        }
        return dictionary;
    }
}
+1
source

All Articles