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

Rya*_*Lee 48 c# hashset

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

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

Jon*_*eet 29

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

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

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

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

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

  • 我真的希望微软能够创建一个带有int的构造函数,即使只是为了使内置集合之间的API更加一致. (4认同)
  • 它将使用集合的大小作为初始容量,但会将多余部分修剪为不超过实际不同元素数量的3倍.因此,如果传入一个包含1M元素的未初始化数组,首先它将在内部创建一个大型数组,但在发现只有一个唯一元素后,它会将其内部数组的大小调整为3个元素. (3认同)

Bar*_*zKP 10

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;
}
Run Code Online (Sandbox Code Playgroud)

这招有效,因为之后ClearHashSet不修剪,如在描述文件:

在拨打电话之前,容量保持不变TrimExcess.

  • 辉煌!可以创建一个公共静态泛型方法以这种方式返回HashSet &lt;T&gt;。不确定创建预分配列表的成本。 (2认同)

Ale*_*kiy 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*erd 5

此功能已在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.
Run Code Online (Sandbox Code Playgroud)

  • 这是自 4.7.2 以来的最佳答案 (2认同)