【问题标题】:what is the fastest way to generate a unique set in .net 2在.net 2中生成唯一集的最快方法是什么
【发布时间】:2010-09-18 23:31:33
【问题描述】:

我有一个本质上是名称值对的锯齿状数组——我需要从中生成一组唯一的名称值。锯齿状数组大约有 86,000 x 11 个值。 我必须以何种方式存储名称值对(单个字符串“name=value”或专门的类,例如 KeyValuePair)对我来说并不重要。
附加信息:有 40 个不同的名称和大量不同的值 - 可能在 10,000 个值区域内。

我正在使用 C# 和 .NET 2.0(性能太差了,我认为将整个锯齿状数组推入 sql 数据库并从那里进行选择可能会更好)。

以下是我当前使用的代码:

List<List<KeyValuePair<string,string>>> vehicleList = retriever.GetVehicles();
this.statsLabel.Text = "Unique Vehicles: " + vehicleList.Count;

Dictionary<KeyValuePair<string, string>, int> uniqueProperties = new Dictionary<KeyValuePair<string, string>, int>();
foreach (List<KeyValuePair<string, string>> vehicle in vehicleList)
{
    foreach (KeyValuePair<string, string> property in vehicle)
    {
        if (!uniqueProperties.ContainsKey(property))
        {
            uniqueProperties.Add(property, 0);
        }
    }
}
this.statsLabel.Text += "\rUnique Properties: " + uniqueProperties.Count;

【问题讨论】:

  • 您能否提供更多关于数据的示例?我不确定我是否理解您在这里尝试做的事情。你想要一套钥匙,还是一对?
  • 我在使用垫子 - 我不太明白锯齿状数组的来源。一些显示输入数据的示例代码会非常方便。
  • 在您的锯齿状数组中,名称和值之间是否存在多对多相关性?您是否试图获得一对一的相关性或一对多的相关性作为输出(再次命名为值)?如果你能回答这个问题,那么我可以提供一个更好的答案。

标签: c# .net performance collections


【解决方案1】:

我让它在 0.34 秒内运行从 9 多分钟缩短

问题在于比较 KeyValuePair 结构时。 我通过编写一个比较器对象并将它的一个实例传递给字典来解决它。

据我所知,KeyValuePair.GetHashCode() 返回它的 Key 对象(在本例中是唯一性最低的对象)的哈希码。

当字典添加(并检查是否存在)每个项目时,它同时使用 Equals 和 GetHashCode 函数,但当哈希码不太唯一时必须依赖 Equals 函数。

通过提供更独特的 GetHashCode 函数,它使用 Equals 函数的频率大大降低。我还优化了 Equals 函数,以便在不那么唯一的键之前比较更独特的值。

86,000 * 11 个具有 10,000 个独特属性的项目使用下面的比较器对象在 0.34 秒内运行(没有比较器对象需要 9 分 22 秒)

希望这会有所帮助:)

    class StringPairComparer
        : IEqualityComparer<KeyValuePair<string, string>>
    {
        public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y)
        {
            return x.Value == y.Value && x.Key == y.Key;
        }
        public int GetHashCode(KeyValuePair<string, string> obj)
        {
            return (obj.Key + obj.Value).GetHashCode();
        }
    }

编辑:如果它只是一个字符串(而不是 KeyValuePair,其中字符串 = 名称 + 值),它的速度大约会快两倍。这是一个很好的有趣的问题,我已经 faaaaaar 花了太多时间在它上面(不过我学会了安静一点)

【讨论】:

    【解决方案2】:

    如果您不需要每个键/值对与您生成的唯一值之间的任何特定关联,您可以只使用 GUID 吗?我假设问题是您当前的“密钥”在这个锯齿状数组中不是唯一的。

    Dictionary<System.Guid, KeyValuePair<string, string>> myDict 
       = new Dictionary<Guid, KeyValuePair<string, string>>();
    
    
    foreach of your key values in their current format
       myDict.Add(System.Guid.NewGuid(), new KeyValuePair<string, string>(yourKey, yourvalue))
    

    听起来它会存储您需要的内容,但我不知道您将如何从中提取数据,因为生成的 Guid 与您最初拥有的内容之间没有语义关系...

    您能否在您的问题中提供更多信息?

    【讨论】:

      【解决方案3】:

      使用 KeyValuePair 作为包装类,然后创建一个字典来创建一个集合?或者实现您自己的覆盖 Equals 和 GetHashCode 的包装器。

      Dictionary<KeyValuePair, bool> mySet;
      
      for(int i = 0; i < keys.length; ++i)
      {
          KeyValuePair kvp = new KeyValuePair(keys[i], values[i]);
          mySet[kvp] = true;
      }
      

      【讨论】:

        【解决方案4】:

        为什么不使用Dictionary,而不是扩展KeyedCollection&lt;TKey, TItem&gt;?根据文档:

        为其键嵌入在值中的集合提供抽象基类。

        然后您需要覆盖protected TKey GetKeyForItem(TItem item) 函数。因为它是IList&lt;T&gt;IDictionary&lt;TKey, TValue&gt; 之间的混合体,我认为它可能会非常快。

        【讨论】:

          【解决方案5】:

          怎么样:

          Dictionary<NameValuePair,int> hs = new Dictionary<NameValuePair,int>();
          foreach (i in jaggedArray)
          {
              foreach (j in i)
              {
                  if (!hs.ContainsKey(j))
                  {
                      hs.Add(j, 0);
                  }
              }
          }
          IEnumerable<NameValuePair> unique = hs.Keys;
          

          当然,如果您使用的是 C# 3.0、.NET 3.5:

          var hs = new HashSet<NameValuePair>();
          hs.UnionWith(jaggedArray.SelectMany(item => item));
          

          会成功的。

          【讨论】:

          • 这几乎就是我目前正在使用的代码 - 不幸的是,大约 20 分钟后,我变得不耐烦并终止了应用程序。
          • 在 C# 3 中,您也可以只使用 .Distinct()
          • @Konrad Rudolph:是的,它会一样慢。
          【解决方案6】:

          您是否分析过您的代码?你确定 foreach 循环是瓶颈,而不是retriever.GetVehicles()?

          我确实创建了一个小型测试项目,我在其中伪造了检索器并让它返回 86.000 X 11 值。我的第一次尝试运行了 5 秒,创建了包含的数据。

          我对键和值都使用了相同的值,其中第一个键是“0#0”,最后一个键是“85999#10”。

          然后我切换到 guid。结果一样。

          然后我把钥匙加长了,像这样:

                  var s = Guid.NewGuid().ToString();
                  return s + s + s + s + s + s + s+ s + s + s;
          

          现在花了将近 10 秒。

          然后我把键弄得非常长,并且出现了内存不足的异常。我的计算机上没有交换文件,所以我立即得到了这个异常。

          你的钥匙有多长?您的虚拟内存消耗是性能不佳的原因吗?

          【讨论】:

          • GetVehicles() 在我的情况下相当快 - 我猜的区别在于数据 - 您的数据将包含所有唯一值,而我的不会 - 仍然令人惊讶的是它的运行速度有多快。外部循环应该是 86,000,内部循环应该是 11。
          猜你喜欢
          • 2010-12-08
          • 1970-01-01
          • 1970-01-01
          • 2013-12-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-05
          • 1970-01-01
          相关资源
          最近更新 更多