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

时间:2011-07-21 06:05:38

标签: c# hashset

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

有些时候我可能会添加很多元素,我想消除调整大小。

5 个答案:

答案 0 :(得分:29)

没有技术上的理由说明为什么这不可能 - 微软只是没有选择公开具有初始容量的构造函数。

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

编辑:我相信如果容量变得比它需要的大,那么构造函数会在找到真正的多少个不同元素时修剪多余的元素。< / p>

无论如何,如果你拥有你将要添加到HashSet<T> 的集合,它会实现ICollection<T>,然后将其传递给构造函数而不是逐个添加元素将是一个胜利,基本上:)

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

答案 1 :(得分:9)

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没有被修剪,如documentation中所述:

  

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

答案 2 :(得分:8)

我使用此代码设置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);
    }
}

UPD。 04 jule

使用反射缓存也可以增强此代码。我们走了:

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;
    }
}

答案 3 :(得分:2)

此功能已在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.

答案 4 :(得分:0)

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