填充Hashset的最快方法

时间:2013-12-10 22:45:08

标签: c# performance hashset

我需要定期浏览大量对象并维护特定String属性的唯一值。

我正在使用Hashset来保存唯一值,但是想知道检查Hashset中是否存在值是否更有效,或者只是尝试添加所有值?

3 个答案:

答案 0 :(得分:2)

由于Jon Hanna所述的原因并且没有给出准确的结果,您的测试结果不佳。当您在内部调用Add HashSet调用AddIfNotPresent时,AddIfNotPresent做的第一件事就是检查对象是否存在(代码来自ILSpy)

public bool Add(T item)
{
    return this.AddIfNotPresent(item);
}

private bool AddIfNotPresent(T value)
{
    if (this.m_buckets == null)
    {
        this.Initialize(0);
    }
    int num = this.InternalGetHashCode(value);
    int num2 = num % this.m_buckets.Length;
    int num3 = 0;
    for (int i = this.m_buckets[num % this.m_buckets.Length] - 1; i >= 0; i = this.m_slots[i].next)
    {
        if (this.m_slots[i].hashCode == num && this.m_comparer.Equals(this.m_slots[i].value, value))
        {
            return false;
        }
        num3++;
    }
    //(Snip)

通过Contains然后Add,您可以检查对象是否存在 两次 。如果存储桶中有许多项目,则检查这可能会导致性能显着下降。

答案 1 :(得分:1)

由于我原来的答案一般受到嘲笑,我还有另外一步。

Int32 maxUniques = 1;
Int32 collectionSize = 100000000;
Random rand = new Random();

while (maxUniques <= collectionSize)
{
    List<Int32> bigCollection = new List<Int32>();
    bigCollection.Capacity = collectionSize;

    for (Int32 count = 0; count < collectionSize; count++)
        bigCollection.Add(rand.Next(maxUniques));

    HashSet<Int32> uniqueSources = new HashSet<Int32>();

    Stopwatch watch = new Stopwatch();
    watch.Start();

    foreach (Int32 num in bigCollection)
    {
        if (!uniqueSources.Contains(num))
            uniqueSources.Add(num);
    }

    Console.WriteLine(String.Format("With {0,10:N0} unique values in a set of {1,10:N0} values, the time taken for conditional add: {2,6:N0} ms", uniqueSources.Count, collectionSize, watch.ElapsedMilliseconds));

    uniqueSources = new HashSet<Int32>();
    watch.Restart();

    foreach (Int32 num in bigCollection)
    {
        uniqueSources.Add(num);
    }

    Console.WriteLine(String.Format("With {0,10:N0} unique values in a set of {1,10:N0} values, the time taken for simple add:      {2,6:N0} ms", uniqueSources.Count, collectionSize, watch.ElapsedMilliseconds));
    Console.WriteLine();

    maxUniques *= 10;
}

其中给出了以下输出:

  

在一组100,000,000个值中有1个唯一值,条件添加所需的时间为:2,004 ms   在一组100,000,000个值中有1个唯一值,简单添加所需的时间:2,540 ms

     

在一组100,000,000个值中有10个唯一值,条件添加所需的时间为:2,066 ms   在一组100,000,000个值中有10个唯一值,简单添加所需的时间为:2,391 ms

     

在一组100,000,000个值中有100个唯一值,条件添加所需的时间为:2,057毫秒   在一组100,000,000个值中有100个唯一值,简单添加所需的时间:2,410 ms

     

在一组100,000,000个值中有1,000个唯一值,条件添加所需的时间为:2,011 ms   在一组100,000,000个值中有1,000个唯一值,简单添加所需的时间:2,459 ms

     

在一组100,000,000个值中有10,000个唯一值,条件添加所需的时间为:2,219 ms
  在一组100,000,000个值中有10,000个唯一值,简单添加所需的时间:2,414 ms

     

在一组100,000,000个值中有100,000个唯一值,条件添加所需的时间为:3,024 ms
  在一组100,000,000个值中有100,000个唯一值,简单添加所需的时间:3,124 ms

     

在一组100,000,000个值中有1,000,000个唯一值,条件添加所需的时间:8,937 ms
  在一组100,000,000个值中有1,000,000个唯一值,简单添加所需的时间为:9,310 ms

     

在一组100,000,000个值中有9,999,536个唯一值,有条件地添加所需的时间:11,798 ms
  在一组100,000,000个值中有9,999,536个唯一值,简单添加所需的时间:11,660 ms

     

在一组100,000,000个值中有63,199,938个唯一值,有条件地添加所需的时间:20,847 ms
  在一组100,000,000个值中有63,199,938个唯一值,简单添加所需的时间:20,213 ms

这对我很好奇。

最多1%的添加,调用Contains()方法更快,而不是只是继续点击Add()。对于10%和63%,只有Add()更快。

换句话说:
100万包含()比9900万添加()更快 1亿个Contains()比9000万个Add()

我调整了代码,以100万增量尝试100万到1000万个唯一值,并发现拐点大约在7-10%之间,结果并不确定。

因此,如果您希望添加的值少于7%,则首先调用Contains()会更快。超过7%,只需调用Add()。

答案 2 :(得分:-1)

当我输入问题时,有人会问我为什么不自己测试。所以我自己测试过。

我创建了一个包含126万条记录和21个独特源代码的集合,并通过以下代码运行:

HashSet<String> uniqueSources = new HashSet<String>();

Stopwatch watch = new Stopwatch();
watch.Start();

foreach (LoggingMessage mess in bigCollection)
{
    uniqueSources.Add(mess.Source);
}

Console.WriteLine(String.Format("Time taken for simple add: {0}ms", watch.ElapsedMilliseconds));

uniqueSources.Clear();
watch.Restart();

foreach (LoggingMessage mess in bigCollection)
{
    if (!uniqueSources.Contains(mess.Source))
        uniqueSources.Add(mess.Source);
}

Console.WriteLine(String.Format("Time taken for conditional add: {0}ms", watch.ElapsedMilliseconds));

结果是:

  

简单添加所花费的时间:147毫秒

     

有条件添加所需的时间:125毫秒

因此,对于我的数据至少,检查存在并不会减慢速度,实际上会稍微快一点。不过它的差异很小。