为什么我不能'< >

本文关键字:不能 为什么 | 更新日期: 2023-09-27 18:03:03

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

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

为什么我不能'< >

以下答案写于2011年。它现在在。net 4.7.2和。net Core 2.0中;它将出现在。net Standard 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

这段代码也可以通过使用反射缓存来增强。

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>的类的实例来构造它,例如List<T>。它将在ICollection<T>上调用Count,分配足够的空间来容纳集合,并将所有元素添加到HashSet中,而不需要重新分配。