【问题标题】:What is the quickest way to compare a C# Dictionary to a 'gold standard' Dictionary for equality?将 C# 字典与“黄金标准”字典进行比较的最快方法是什么?
【发布时间】:2011-04-22 15:03:41
【问题描述】:

我有一个已知良好的字典,在运行时我需要创建一个新字典并运行检查以查看它是否具有与已知良好字典相同的键值对(可能以不同的顺序插入) ,如果是则走一条路,否则走另一条路。我不一定需要序列化整个已知良好的字典(例如,我可以使用哈希),但我需要一些磁盘上的数据,这些数据具有关于已知良好字典的足够信息,以便进行比较,如果没有的话娱乐。最快的方法是什么?我可以使用 SortedDictionary,但初始化和添加值所需的时间会影响此任务的速度。

具体例子:

考虑一个看起来像这样的 Dictionary<String,List<String>>(显然没有特定的顺序):

{ {"key1", {"value1", "value2"} }, {"key2", {"value3", "value4"} } }  

我创建了一次字典,并在磁盘上保存了某种形式的信息(完整的序列化、哈希等)。然后,在运行时,我执行以下操作:

Dictionary<String,List<String>> d1 = new Dictionary<String,List<String>> ();
Dictionary<String,List<String>> d2 = new Dictionary<String,List<String>> ();
Dictionary<String,List<String>> d3 = new Dictionary<String,List<String>> ();

String key11 = "key1";
String key12 = "key1";
String key13 = "key1";
String key21 = "key2";
String key22 = "key2";
String key23 = "key2";

List<String> value11 = new List<String> {"value1", "value2"};
List<String> value12 = new List<String> {"value1", "value2"};
List<String> value13 = new List<String> {"value1", "value2"};
List<String> value21 = new List<String> {"value3", "value4"};
List<String> value22 = new List<String> {"value3", "value4"};
List<String> value23 = new List<String> {"value3", "value5"};

dict1.add(key11, value11);
dict1.add(key21, value21);
dict2.add(key22, value22);
dict2.add(key12, value12);
dict3.add(key13, value13);
dict3.add(key23, value23);

dict1.compare(fileName); //Should return true
dict2.compare(fileName); //Should return true
dict3.compare(fileName); //Should return false

同样,如果从启动到从 compare() 返回的总时间更快,我可以更改此代码以使用 SortedDictionary(或其他任何东西),但我不能保证排序,我需要一些一致的比较. compare() 可以加载序列化并遍历字典,它可以序列化内存中的字典并将序列化与文件名进行比较,或者它可以做任何其他事情。

【问题讨论】:

  • 请确认您正在寻求“价值平等”作为比较?问题的措辞方式我相信是这样,但我想确定。
  • 在优化这样的任务时,明确您正在优化的情况会很有帮助。需要优化的常见情况是两个字典相同还是不同?我问的原因是因为一种常见的优化技术是做一个廉价的测试来捕捉 99% 的“不同”案例,然后做一个昂贵的测试来确认身份。如果大多数时候字典确实是相同的,那么显然这不是一个好的优化;廉价的测试只会让你慢下来。
  • Cos Callis:如果对于已知良好字典中的每个 String-List 对,在要检查的字典中有一个 String-List 对具有相同的值字符串作为键和一个具有相同值的字符串的列表与值的顺序相同,那么我想要true,否则为false。
  • Eric Lippert:我希望字典能够在 90% 的情况下匹配,如果它们不匹配,我必须执行昂贵且耗时的启动。
  • @Shea:当然,但这不是我问的。您可以在同一进程中创建两个具有不同策略的字典;我每天都这样做。我们知道字典有相同的政策吗?当两个字典有不同的策略时,不清楚意味着什么是平等的。例如,假设一个字典是 "A"-->{"DEF"} 并且不区分大小写,而另一个字典是 "a"-->{"DEF"}, "A"-->{"DEF"},区分大小写。可以说,这两个字典是相同的; 他们总是同意每个查询。但是它们的大小不同!

标签: c# serialization dictionary


【解决方案1】:

解决方案一:使用集合相等。

如果字典大小不同,你就知道它们是不相等的。

如果它们的大小相同,则从一个字典中构建一组可变哈希键。从中删除其他字典中的所有键。如果您尝试删除不存在的密钥,则密钥集是不相等的,您知道哪个密钥是问题所在。

或者,构建两个哈希集并取它们的交集;生成的交集应该是原始集合的大小。

这需要 O(n) 时间和 O(n) 空间。

一旦您知道键集相等,然后一次遍历所有键,获取值,然后比较值。由于值是序列,请使用 SequenceEquals。这需要 O(n) 时间和 O(1) 空间。

解决方案二:对键进行排序

同样,如果字典大小不同,您就知道它们是不相等的。

如果它们的大小相同,则对两组键进行排序并对其执行 SequenceEquals;如果键的序列不相等,则字典不相等。

这需要 O(n lg n) 时间和 O(n) 空间。

如果成功,则再次遍历键并比较值。

解决方案三:

再次检查字典,看看它们的大小是否相同。

如果是,则遍历一个字典的键并检查该键是否存在于另一字典中。如果不是,则它们不相等。如果是,则检查相应的值是否相等。

这在时间上是 O(n),在空间上是 O(1)。

如何在这些可能的解决方案中进行选择?这取决于可能的故障模式是什么,以及您是否需要知道丢失或额外的密钥是什么。如果可能的故障模式是一个坏密钥,那么选择一个专注于首先找到坏密钥的解决方案可能会更有效率,并且只有在所有密钥都正常时才检查坏值。如果可能的故障模式是错误值,那么第三种解决方案可能是最好的,因为它优先考虑及早检查值。

【讨论】:

  • +1 一如既往的详尽解释。解决方案 3 是我首先想到的。
【解决方案2】:

由于我的 cmets 接受的答案,这里有一个更严格的检查。

goodDictionary.Keys.All(k=>
    {
        List<string> otherVal;
        if(!testDictionary.TryGetValue(k,out otherVal))
        {
            return false;
        }
        return goodDictionary[k].SequenceEquals(otherVal);
    })

【讨论】:

  • 这有同样的问题,只是切换:testDictionary 可以有比 goodDictionary 更多的键。解决方案是在任何循环之前检查字典大小。
  • @Shea,是的,错过了那个案例,但确实有进步
【解决方案3】:

如果您已经进行了序列化,则获取每个序列化字典的哈希值(我推荐 SHA-1),然后进行比较。

【讨论】:

  • 我可以相信序列化是一致的吗?
  • 是的,如果您编写自己的序列化器。我会首先对字典的所有键进行排序,然后对每个键、值对进行序列化。
【解决方案4】:

我不认为这里有灵丹妙药;您只需要查找每个密钥对:

public bool IsDictionaryAMatch(Dictionary<string, List<string>> dictionaryToCheck)
{
    foreach(var kvp in dictionaryToCheck)
    {
         // Do the Keys Match
         if(!goodDictionary.Exists(x => x.Key == kvp.Key))
             return false;

         foreach(var valueElement in kvp.Value)
         {
              // Do the Values in each list match
              if(!goodDictionary[kvp.Key].Exists(x => x == valueElement))
                  return false;
         }
    }

    return true;
}

【讨论】:

  • 我认为这是最好的选择(甚至不需要这样排序)。我要添加的一件事是检查字典在 foreach 循环之前是否具有相同的大小。
  • 这将允许好字典中的虚假条目。假设测试字典的键 x 包含 1,2,3,4 并且好字典的键 x 包含 1,2,3,4,5,6,7,8 那么此方法将声明匹配。这是被问到的吗?它也不强制列表的顺序,即 1,2,3,4 将匹配 4,3,2,1。
  • spender:这就是为什么我在评论中指出我会添加一个检查以查看字典是否具有相同的大小。但答案的精神似乎是最好的选择。
  • @Shea Levy, @Tejs:这段代码显然是错误的。假设要检查的字典是空字典。循环被跳过,函数返回 true。即使您解决了这个问题,此代码在大型字典或列表值很长的情况下效率极低
【解决方案5】:

嗯,在某些时候你需要比较每个键是否具有相同的值,但在此之前你可以做一些快速的事情,比如检查每个字典有多少键,然后检查键列表是否匹配。这些应该相当快,如果其中任何一个测试失败,您可以中止更昂贵的测试。

之后,您可以构建单独的键列表,然后触发 Paraells 查询以比较实际值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-03
    相关资源
    最近更新 更多