繁体   English   中英

填充Hashset的最快方法

[英]Fastest way to populate a Hashset

我需要定期遍历大量对象并维护其中特定String属性的唯一值。

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

你的测试是一个糟糕的测试,因为Jon Hanna说的原因并没有给你准确的结果。 当你调用Add AddIfNotPresent HashSet调用AddIfNotPresentAddIfNotPresent做的第一件事是检查对象是否存在(代码来自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您可以检查对象是否存在两次 如果您在存储桶中有许多项目,则检查这可能会导致严重的性能损失。

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

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 ms在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()更快。

换一种方式:
1亿个Contains()比9900万个Add()更快
1亿个Contains()比9000万个Add()慢

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

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

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

我创建了一个包含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毫秒

所以至少对我的数据来说,检查存在并不会减慢速度,实际上会稍快一些。 不过它的差异很小。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM