繁体   English   中英

为什么我不能预先分配一个哈希集<t></t>

[英]Why can't I preallocate a hashset<T>

为什么我不能预先分配一个hashset<T>

有时我可能会向其中添加很多元素并且我想消除调整大小。

下面的答案写于 2011 年。现在在 .NET 4.7.2 和 .NET Core 2.0 中; 它将在 .NET 标准 2.1 中。


这不应该是不可能的,没有技术上的原因——微软只是没有选择公开具有初始容量的构造函数。

如果您可以调用采用IEnumerable<T>的构造函数并使用ICollection<T>的实现,我相信它将使用集合的大小作为初始最小容量。 请注意,这是一个实现细节。 容量只需大到足以存储所有不同的元素......

编辑:我相信如果容量结果比它需要的大得多,构造函数将在完成找出真正有多少不同元素后修剪多余的部分。

无论如何,如果您要添加到HashSet<T>的集合并且它实现了ICollection<T> ,那么将它传递给构造函数而不是一个一个地添加元素将是一个胜利,基本上: )

编辑:一种解决方法是使用Dictionary<TKey, TValue>而不是HashSet<T> ,而不是使用这些值。 但这并不适用于所有情况,因为它不会为您提供与HashSet<T>相同的接口。

Jon Skeet 的回答几乎是完整的。 要使用HashSet<int>解决此问题,我必须执行以下操作:

public class ClassUsingHashSet
{
    private static readonly List<int> PreallocationList
        = Enumerable.Range(0, 10000).ToList();

    public ClassUsingHashSet()
    {
        this.hashSet = new HashSet<int>(PreallocationList);
        this.hashSet.Clear();
    }

    public void Add(int item)
    {
        this.hashSet.Add(item);
    }

    private HashSet<int> hashSet;
}

这个技巧有效,因为在Clear HashSet之后没有修剪,如文档中所述:

在调用TrimExcess之前,容量保持不变。

我正在使用此代码来设置 HashSet 的初始容量。 您可以将其用作扩展程序或直接使用

public static class HashSetExtensions
{
    private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic;
    public static HashSet<T> SetCapacity<T>(this HashSet<T> hs, int capacity)
    {
        var initialize = hs.GetType().GetMethod("Initialize", Flags);
        initialize.Invoke(hs, new object[] { capacity });
        return hs;
    }

    public static HashSet<T> GetHashSet<T>(int capacity)
    {
        return new HashSet<T>().SetCapacity(capacity);
    }
}

更新。 04 朱尔

也可以通过使用反射缓存来增强此代码。 这里我们 go:

public static class HashSetExtensions
{
    private static class HashSetDelegateHolder<T>
    {
        private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic;
        public static MethodInfo InitializeMethod { get; } = typeof(HashSet<T>).GetMethod("Initialize", Flags);
    }

    public static void SetCapacity<T>(this HashSet<T> hs, int capacity)
    {
        HashSetDelegateHolder<T>.InitializeMethod.Invoke(hs, new object[] { capacity });
    }

    public static HashSet<T> GetHashSet<T>(int capacity)
    {
        var hashSet = new HashSet<T>();
        hashSet.SetCapacity(capacity);
        return hashSet;
    }
}

此功能在4.7.2中添加:

HashSet<T>(Int32)

Initializes a new instance of the HashSet<T> class that is empty, 
but has reserved space for capacity items and uses the default 
equality comparer for the set type.

使用初始容量初始化 HashSet 的唯一方法是使用实现ICollection<T>的 class 的实例(例如List<T> )来构造它。 它将调用ICollection<T>上的 Count 分配足够的空间来保存集合并将所有元素添加到 HashSet 而不重新分配。

暂无
暂无

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

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