簡體   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