简体   繁体   English

为什么我不能预先分配一个哈希集<t></t>

[英]Why can't I preallocate a hashset<T>

Why can't I preallocate a hashset<T> ?为什么我不能预先分配一个hashset<T>

There are times when i might be adding a lot of elements to it and i want to eliminate resizing.有时我可能会向其中添加很多元素并且我想消除调整大小。

Answer below was written in 2011. It's now in .NET 4.7.2 and .NET Core 2.0;下面的答案写于 2011 年。现在在 .NET 4.7.2 和 .NET Core 2.0 中; it will be in .NET Standard 2.1.它将在 .NET 标准 2.1 中。


There's no technical reason why this shouldn't be possible - Microsoft just hasn't chosen to expose a constructor with an initial capacity.这不应该是不可能的,没有技术上的原因——微软只是没有选择公开具有初始容量的构造函数。

If you can call a constructor which takes an IEnumerable<T> and use an implementation of ICollection<T> , I believe that will use the size of the collection as the initial minimum capacity.如果您可以调用采用IEnumerable<T>的构造函数并使用ICollection<T>的实现,我相信它将使用集合的大小作为初始最小容量。 This is an implementation detail, mind you.请注意,这是一个实现细节。 The capacity only has to be large enough to store all the distinct elements...容量只需大到足以存储所有不同的元素......

EDIT: I believe that if the capacity turns out to be way larger than it needs to be, the constructor will trim the excess when it's finished finding out how many distinct elements there really are.编辑:我相信如果容量结果比它需要的大得多,构造函数将在完成找出真正有多少不同元素后修剪多余的部分。

Anyway, if you have the collection you're going to add to the HashSet<T> and it implements ICollection<T> , then passing it to the constructor instead of adding the elements one by one is going to be a win, basically:)无论如何,如果您要添加到HashSet<T>的集合并且它实现了ICollection<T> ,那么将它传递给构造函数而不是一个一个地添加元素将是一个胜利,基本上: )

EDIT: One workaround would be to use a Dictionary<TKey, TValue> instead of a HashSet<T> , and just not use the values.编辑:一种解决方法是使用Dictionary<TKey, TValue>而不是HashSet<T> ,而不是使用这些值。 That won't work in all cases though, as it won't give you the same interface as HashSet<T> .但这并不适用于所有情况,因为它不会为您提供与HashSet<T>相同的接口。

The answer by Jon Skeet is almost a complete one. Jon Skeet 的回答几乎是完整的。 To solve this problem with HashSet<int> I had to do the following:要使用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;
}

This trick works because after Clear the HashSet is not trimmed, as described in the documentation :这个技巧有效,因为在Clear HashSet之后没有修剪,如文档中所述:

The capacity remains unchanged until a call to TrimExcess is made.在调用TrimExcess之前,容量保持不变。

I'm using this code to set initial capacity for HashSet.我正在使用此代码来设置 HashSet 的初始容量。 You can use it as 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);
    }
}

upd.更新。 04 jule 04 朱尔

This code may be also enhanced by using reflection caching.也可以通过使用反射缓存来增强此代码。 Here we go:这里我们 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 capability was added in 4.7.2 :此功能在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 the HashSet with an initial capacity is to construct it with a instnace of a class, such as a List<T> , that implements ICollection<T> .使用初始容量初始化 HashSet 的唯一方法是使用实现ICollection<T>的 class 的实例(例如List<T> )来构造它。 It will call Count on the ICollection<T> allocate enough space to hold the collection and add all the elements to the HashSet without reallocation.它将调用ICollection<T>上的 Count 分配足够的空间来保存集合并将所有元素添加到 HashSet 而不重新分配。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我不能写if(object是HashSet &lt;&gt;)但是如果我写的话就没关系(object.GetType()== typeof(HashSet &lt;&gt;)) - Why can't I write if (object is HashSet<>) but it's okay if I write (object.GetType() == typeof(HashSet<>)) 为什么我不能在没有枚举的情况下从HashSet中检索项目? - Why can't I retrieve an item from a HashSet without enumeration? 为什么找不到此HashSet的Sum()。 说:“算术运算导致溢出。” - Why can't I find Sum() of this HashSet. says “Arithmetic operation resulted in an overflow.” 为什么我不能使用HashSet <string> 实现一个IEnumerable <string> 接口属性? - Why can't I use HashSet<string> to implement an IEnumerable<string> interface property? 为什么是HashSet <T> .IsReadOnly明确吗? - Why is HashSet<T>.IsReadOnly explicit? 为什么HashSet <T> 没有实现IReadOnlyCollection <T> ? - Why HashSet<T> does not implement IReadOnlyCollection<T>? 如何从HashSet List中删除“\\ t”? - How can i remove “\t” from HashSet List? ICollection <T> 为什么在HashSet上调用SerializationBinder <T> 但不在清单上 <T> - ICollection<T> Why SerializationBinder is call on HashSet<T> but not on List<T> 我们不能在HashSet中使用StringComparison吗? - Can't we use StringComparison in HashSet? 为什么我需要转换IDictionary <T, HashSet<S> &gt;到IDictionary <T, IEnumerable<S> &gt;? - Why do I need to cast IDictionary<T, HashSet<S>> to IDictionary<T, IEnumerable<S>>?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM