Why I can not redistribute hashset <T>
Why can't I pre-allocate hashset<T> ?
There are times when I can add a lot of elements to it, and I want to exclude resizing.
The answer below was written in 2011. Now it is in .NET 4.7.2 and .NET Core 2.0; it will be in .NET Standard 2.1.
There is no technical reason why this should not be possible - Microsoft simply did not want to expose the constructor with the initial capacity.
If you can call a constructor that accepts IEnumerable<T> and use the implementation of ICollection<T> , I believe that the collection size will be used as the initial minimum capacity. Pay attention to the implementation details. The capacity should be large enough to store all the individual elements ...
EDIT: I believe that if the capacity turns out to be much larger than necessary, the constructor will trim the excess when it finishes to find out how many different elements are in fact.
In any case, if you have a collection that you are going to add to the HashSet<T> and it implements ICollection<T> , then passing it to the constructor instead of adding elements one by one will be advantageous, mainly :)
EDIT: One workaround would be to use Dictionary<TKey, TValue> instead of HashSet<T> , and just not use the values. This will not work in all cases, as it will not give you the same interface as the HashSet<T> .
John Skeet's answer is almost complete. To solve this problem with a HashSet<int> I had to do the following:
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; } This trick works because after Clear HashSet not cropped as described in the documentation :
Capacity remains unchanged until
TrimExcess.
I use this code to set the initial capacity for a HashSet. You can use it as an extension or directly
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); } } update 04 jule
This code can also be enhanced with reflection caching. Here we 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; } } This feature was added in 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. The only way to initialize a HashSet with initial capacity is to build it using a class element, such as List<T> , that implements ICollection<T> . It will call Count on ICollection<T> to allocate enough space to store the collection and add all the elements to the HashSet without reallocation.