[英]Why can't I write if (object is HashSet<>) but it's okay if I write (object.GetType() == typeof(HashSet<>))
[英]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.