簡體   English   中英

是否有內置方法來比較集合?

[英]Is there a built-in method to compare collections?

我想在我的 Equals 方法中比較幾個集合的內容。 我有一個字典和一個 IList。 有沒有內置的方法來做到這一點?

編輯:我想比較兩個字典和兩個 IList,所以我認為相等的含義很清楚 - 如果兩個字典包含映射到相同值的相同鍵,那么它們是相等的。

Enumerable.SequenceEqual

通過使用指定的 IEqualityComparer(T) 比較它們的元素來確定兩個序列是否相等。

您不能直接比較列表和字典,但可以將字典中的值列表與列表進行比較

正如其他人所建議和指出的, 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))

除了提到的SequenceEqual 之外

如果兩個列表的長度相等並且它們對應的元素根據比較器比較相等,則為真

(這可能是默認的比較器,即覆蓋Equals()

值得一提的是,在.Net4 中ISet對象上有ISet

忽略元素的順序和任何重復的元素。

因此,如果您想要一個對象列表,但它們不需要按特定順序排列,請考慮ISet (如HashSet )可能是正確的選擇。

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

這不是直接回答您的問題,但是 MS 的 TestTools 和 NUnit 都提供

 CollectionAssert.AreEquivalent

這幾乎可以滿足您的需求。

.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

我不知道 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 之后,看起來微軟已經構建了我為您描述的功能。

要比較集合,您還可以使用 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

對於有序集合(列表、數組)使用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>

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

不,因為框架不知道如何比較列表的內容。

看看這個:

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

沒有,沒有,也可能沒有,至少我會這么認為。 背后的原因是集合相等可能是用戶定義的行為。

集合中的元素不應該按特定順序排列,盡管它們確實具有自然排序,但這不是比較算法應該依賴的。 假設您有兩個集合:

{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的屬性實現

  • 如果引用相等,那就是鮑里斯

我已經制定了自己的比較方法。 它返回常見的、缺失的和額外的值。

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

不。集合框架沒有任何平等的概念。 如果您考慮一下,沒有辦法比較非主觀的集合。 例如,將您的 IList 與您的字典進行比較,如果所有鍵都在 IList 中,所有值都在 IList 中,或者如果兩者都在 IList 中,它們是否相等? 在不知道它們的用途的情況下,沒有明顯的方法來比較這兩個集合,因此通用的 equals 方法沒有意義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM