c#HashSet init需要太长时间

时间:2017-11-19 14:04:35

标签: c#

我正在处理这样一个事实:我需要使用一组元素初始化一个HashSet,但没有任何类型的比较类。

在init之后,添加到HashSet的任何元素都需要与比较器一起传递。

我怎样才能完成它?

现在我有了这个:

HashSet<Keyword> set = new HashSet<Keyword>(new KeyWordComparer());

问题在于,init需要很长时间,并且没有必要应用比较。

KeywordComparer类

class KeyWordComparer : EqualityComparer<Keyword>
{
    public override bool Equals(Keyword k1, Keyword k2)
    {
        int equals = 0;
        int i = 0;
        int j = 0;

        // based on sorted ids
        while (i < k1._lista_modelos.Count && j < k2._lista_modelos.Count)
        {
            if (k1._lista_modelos[i] < k2._lista_modelos[j])
            {
                i++;
            }
            else if (k1._lista_modelos[i] > k2._lista_modelos[j])
            {
                j++;
            }
            else
            {
                equals++;
                i++;
                j++;
            }
         }

         return equals >= 8;
    }
    public override int GetHashCode(Keyword keyword)
    {
        return 0;//notice that using the same hash for all keywords gives you an O(n^2) time complexity though.
    }
}

注意:这是c# comparing list of IDs的后续问题。

2 个答案:

答案 0 :(得分:1)

  

每个关键字都有20个ID,因此当我想向HashSet添加新的关键字时,KeywordComparer会检查新的关键字与HashSet的任何关键字重复的ID不超过8个。如果不包含新关键字,则不包括在内。

收集这些关键字不是此处哈希集的工作。哈希集通常不适用于依赖于集合的其他元素的项目。您应该只将它用于可以为每个项计算有用哈希的事项。由于它取决于现有项目集是否将新项目添加到您的集合中,这完全是错误的工具。

根据您对实际操作的简短描述,尝试解决此问题。在这里,我们只是在列表中收集关键字。为了验证它们是否可以添加,我们使用附加哈希集来收集关键字的ID 。这样,我们可以快速检查新项目,其中8个或更多的ID是否已包含在关键字列表中。

var keywords = new List<Keyword>();
var selectedIds = new HashSet<int>(); // I’m assuming that the ids are ints here

foreach (var keyword in GetListOfAllKeywords())
{
    // count the number of keyword ids that are already in the selectedIds set
    var duplicateIdCount = keyword.Ids.Count(id => selectedIds.Contains(id));

    if (duplicateIdCount <= 8)
    {
        // less or equal to 8 ids are already selected, so add this keyword
        keywords.Add(keyword);

        // and collect all the keyword’s ids
        selectedIds.AddRange(keyword.Ids);
    }
}

答案 1 :(得分:0)

如果我远离这个事实,如果使用HashSet是适合手头工作的类型,或者如果你的Comparer甚至是有意义的,那么实现一个合适的GetHashCode确实会产生巨大的差异。

以下是基于answerMarc Gravell的示例实现:

class KeyWordComparer : EqualityComparer<Keyword>
{
    // omitted your Equals implentaton for brevity

    public override int GetHashCode(Keyword keyword)
    {
        //return 0; // this was the original
        // Marc Gravell https://stackoverflow.com/a/371348/578411
        int hash = 13;
        // not sure what is up with the only 8 ID's but I take that as a given
        for(var i=0; i < Math.Min(keyword._lista_modelos.Count, 8) ; i++)
        {
            hash = (hash * 7) + keyword._lista_modelos[i].GetHashCode();
        }
        return hash;
    }
}

当我使用此测试装置在LinqPad中运行时

   Random randNum = new Random();
   var kc = new KeyWordComparer();
   HashSet<Keyword> set = new HashSet<Keyword>(kc);
   var sw = new Stopwatch();
   sw.Start();
   for(int i =0 ; i< 10000; i++)
   {
     set.Add(new Keyword(Enumerable
            .Repeat(0, randNum.Next(1,10))
            .Select(ir => randNum.Next(1, 256)).ToList()));
   }
   sw.Stop();
   sw.ElapsedMilliseconds.Dump("ms");

这是我衡量的:

  • 10,000件物品的7毫秒

如果我切换回return 0;实施GetHashCode I measure

  • 10,000件物品的4754毫秒

如果我增加了testloop以插入100,000个项目,那么更好的GetHashCode仍会在224毫秒内完成。我没等你的实施完成。

因此,如果有任何实现正确的GetHashCode方法。