代码之家  ›  专栏  ›  技术社区  ›  rony l

C语言中词典之间的相等性测试#

  •  53
  • rony l  · 技术社区  · 14 年前

    假设字典的键和值都有它们的equal和hash方法,那么测试两个字典是否相等的最简洁和有效的方法是什么?

    在这种情况下,如果两个字典包含相同的键集(顺序不重要),则它们被认为是相等的,并且对于每一个这样的键,它们在值上都是一致的。

    以下是我想出的一些方法(可能还有更多方法):

    public bool Compare1<TKey, TValue>(
        Dictionary<TKey, TValue> dic1, 
        Dictionary<TKey,TValue> dic2)
    {
        return dic1.OrderBy(x => x.Key).
            SequenceEqual(dic2.OrderBy(x => x.Key));
    }
    
    public bool Compare2<TKey, TValue>(
        Dictionary<TKey, TValue> dic1, 
        Dictionary<TKey, TValue> dic2)
    {
        return (dic1.Count == dic2.Count && 
            dic1.Intersect(dic2).Count().
            Equals(dic1.Count));
    }
    
    public bool Compare3<TKey, TValue>(
        Dictionary<TKey, TValue> dic1, 
        Dictionary<TKey, TValue> dic2)
    {
        return (dic1.Intersect(dic2).Count().
            Equals(dic1.Union(dic2).Count()));
    }
    
    9 回复  |  直到 6 年前
        1
  •  89
  •   Nick Jones    14 年前
    dic1.Count == dic2.Count && !dic1.Except(dic2).Any();
    
        2
  •  12
  •   LukeH    10 年前

    这真的取决于你所说的平等。

    此方法将测试两个字典是否包含具有相同值的相同键(假设两个字典使用相同的键) IEqualityComparer<TKey> 实施)。

    public bool CompareX<TKey, TValue>(
        Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2)
    {
        if (dict1 == dict2) return true;
        if ((dict1 == null) || (dict2 == null)) return false;
        if (dict1.Count != dict2.Count) return false;
    
        var valueComparer = EqualityComparer<TValue>.Default;
    
        foreach (var kvp in dict1)
        {
            TValue value2;
            if (!dict2.TryGetValue(kvp.Key, out value2)) return false;
            if (!valueComparer.Equals(kvp.Value, value2)) return false;
        }
        return true;
    }
    
        3
  •  3
  •   Lee    14 年前

    您可以使用LINQ进行键/值比较:

    public bool Compare<TKey, TValue>(Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue dict2)
    {
        IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default;
    
        return  dict1.Count == dict2.Count &&
                dict1.Keys.All(key => dict2.ContainsKey(key) && valueComparer.Equals(dict1[key], dict2[key]));
    }
    
        4
  •  1
  •   Community CDub    8 年前

    @Allen's answer :

    bool equals = a.Intersect(b).Count() == a.Union(b).Count()
    

    是关于数组的,但是 IEnumerable<T> 方法可以用于 Dictionary<K,V> 也是。

        5
  •  1
  •   Machtyn Vitalii Fedorenko    10 年前

    根据我在SmartHelp中为except方法所读的内容,我认为公认的答案是正确的:“通过使用默认的相等比较器比较值来产生两个序列的设置差异。”但我发现这不是一个好答案。

    考虑此代码:

    Dictionary<string, List<string>> oldDict = new Dictionary<string, List<string>>()
        {{"001A", new List<string> {"John", "Doe"}},
         {"002B", new List<string> {"Frank", "Abignale"}},
         {"003C", new List<string> {"Doe", "Jane"}}};
    Dictionary<string, List<string>> newDict = new Dictionary<string, List<string>>()
        {{"001A", new List<string> {"John", "Doe"}},
         {"002B", new List<string> {"Frank", "Abignale"}},
         {"003C", new List<string> {"Doe", "Jane"}}};
    
    bool equal = oldDict.Count.Equals(newDict.Count) && !oldDict.Except(newDict).Any();
    Console.WriteLine(string.Format("oldDict {0} newDict", equal?"equals":"does not equal"));
    equal = oldDict.SequenceEqual(newDict);
    Console.WriteLine(string.Format("oldDict {0} newDict", equal ? "equals" : "does not equal"));
    
    Console.WriteLine(string.Format("[{0}]", string.Join(", ", 
        oldDict.Except(newDict).Select(k => 
            string.Format("{0}=[{1}]", k.Key, string.Join(", ", k.Value))))));
    

    结果如下:

    oldDict does not equal newDict
    oldDict does not equal newDict
    [001A=[John, Doe], 002B=[Frank, Abignale], 003C=[Doe, Jane]]
    

    如您所见,“olddict”和“newdict”的设置完全相同。建议的解决方案和调用SequenceEqual都不能正常工作。我想知道这是不是由于使用了延迟加载或字典的比较器设置方式以外的原因。(尽管如此,从结构和参考解释来看,应该这样做。)

    这是我提出的解决方案。请注意,我使用的规则如下:如果两个字典都包含相同的键,并且每个键的值匹配,则两个字典是相等的。键和值的顺序必须相同。我的解决方案可能不是最有效的,因为它依赖于遍历整个键集。

    private static bool DictionaryEqual(
        Dictionary<string, List<string>> oldDict, 
        Dictionary<string, List<string>> newDict)
    {
        // Simple check, are the counts the same?
        if (!oldDict.Count.Equals(newDict.Count)) return false;
    
        // Verify the keys
        if (!oldDict.Keys.SequenceEqual(newDict.Keys)) return false;
    
        // Verify the values for each key
        foreach (string key in oldDict.Keys)
            if (!oldDict[key].SequenceEqual(newDict[key]))
                return false;
    
        return true;
    }
    

    如果出现以下情况,还可以查看结果如何变化: 键顺序不同。(返回false)

    newDict = new Dictionary<string, List<string>>()
        {{"001A", new List<string> {"John", "Doe"}},
         {"003C", new List<string> {"Doe", "Jane"}},
         {"002B", new List<string> {"Frank", "Abignale"}}};
    

    和 键顺序匹配,但值不匹配(返回false)

    newDict = new Dictionary<string, List<string>>()
        {{"001A", new List<string> {"John", "Doe"}},
         {"002B", new List<string> {"Frank", "Abignale"}},
         {"003C", new List<string> {"Jane", "Doe"}}};
    

    如果序列顺序不重要,可以将函数更改为以下值,但可能会影响性能。

    private static bool DictionaryEqual_NoSort(
        Dictionary<string, List<string>> oldDict,
        Dictionary<string, List<string>> newDict)
    {
        // Simple check, are the counts the same?
        if (!oldDict.Count.Equals(newDict.Count)) return false;
    
        // iterate through all the keys in oldDict and
        // verify whether the key exists in the newDict
        foreach(string key in oldDict.Keys)
        {
            if (newDict.Keys.Contains(key))
            {
                // iterate through each value for the current key in oldDict and 
                // verify whether or not it exists for the current key in the newDict
                foreach(string value in oldDict[key])
                    if (!newDict[key].Contains(value)) return false;
            }
            else { return false; }
        }
    
        return true;
    }
    

    使用以下for newdict检查字典是否等于nosort(字典等于nosort返回true):

    newDict = new Dictionary<string, List<string>>()
        {{"001A", new List<string> {"John", "Doe"}},
         {"003C", new List<string> {"Jane", "Doe"}},
         {"002B", new List<string> {"Frank", "Abignale"}}};     
    
        6
  •  1
  •   Jason Masters    6 年前

    除了@nick jones answer,您还需要以同样的顺序不可知的方式实现gethashcode。我建议这样做:

    public override int GetHashCode()
    {
            int hash = 13;
            var orderedKVPList = this.DictProp.OrderBy(kvp => kvp.key)
            foreach (var kvp in orderedKVPList)
            {
                     hash = (hash * 7)  + kvp.Key.GetHashCode();
                     hash = (hash * 7)  + kvp.value.GetHashCode();
            }
            return hash;
    }
    
        7
  •  0
  •   supercat    14 年前

    如果两个字典包含相同的键,但顺序不同,是否应认为它们相等?如果没有,那么应该通过同时运行枚举器来比较字典。这可能比通过一个字典枚举和在另一个字典中查找每个元素更快。如果您事先知道相同的字典的元素顺序相同,那么这样的双重枚举可能是解决问题的方法。

        8
  •  0
  •   Community CDub    8 年前

    在运筹学问题中,它确实指出,相等性测试不仅应包括匹配键,还应包括它们的值。” 在这种情况下,如果两个字典包含相同的键集(顺序不重要),则称它们相等,并且 对于每一把这样的钥匙,他们都同意它的价值 ."

    我是缺了什么东西还是有标记的答案? https://stackoverflow.com/a/3804852/916121 只检查大小和键是否相等,而不检查它们的值?

    我本来会把这个贴在答案旁边的,但没办法把它作为评论添加,对不起。

        9
  •  0
  •   NullVoxPopuli    10 年前

    对于嵌套字典和列表,我在这里结合了以下几点想法: https://gist.github.com/NullVoxPopuli/f95baaa48b4e9854dcfe (太多的代码无法在这里发布)~100行