Почему я не могу предварительно выделить хэш-сет <T> - PullRequest
48 голосов
/ 21 июля 2011

Почему я не могу предварительно выделить hashset<T>?

Бывают случаи, когда я могу добавить к нему много элементов и хочу устранить изменение размера.

Ответы [ 5 ]

29 голосов
/ 21 июля 2011

Ответ ниже был написан в 2011 году. Теперь он в .NET 4.7.2 и .NET Core 2.0;это будет в .NET Standard 2.1.


Нет технической причины, по которой это невозможно, - Microsoft просто не решила представить конструктор с начальной емкостью.

Если вы можете вызвать конструктор, который принимает IEnumerable<T> и использовать реализацию ICollection<T>, я считаю, что в качестве начальной минимальной емкости будет использоваться размер коллекции.Обратите внимание на детали реализации.Емкость должна быть достаточно большой, чтобы хранить все отдельные элементы ...

РЕДАКТИРОВАТЬ: Я считаю, что если емкость оказывается намного больше, чем нужно, конструктор будет обрезать избыток, когдаона закончила выяснять, сколько различных элементов действительно .

В любом случае, если у вас есть коллекция, которую вы собираетесь добавить к HashSet<T> и он реализует ICollection<T>, тогда передача его конструктору вместо добавления элементов один за другим будет выигрышем, в основном:)

РЕДАКТИРОВАТЬ: Одним из обходных путей будет использованиеDictionary<TKey, TValue> вместо HashSet<T> и просто не использовать значения.Это не будет работать во всех случаях, так как не даст вам тот же интерфейс, что и HashSet<T>.

9 голосов
/ 15 апреля 2014

Ответ Джона Скита почти полный.Чтобы решить эту проблему с 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.

8 голосов
/ 27 июня 2014

Я использую этот код для установки начальной емкости для 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);
    }
}

upd.04 июля

Этот код также может быть улучшен за счет использования кэширования отражений.Вот и мы:

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;
    }
}
3 голосов
/ 12 июля 2018

Эта возможность была добавлена ​​в 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.
0 голосов
/ 21 июля 2011

Единственный способ инициализировать HashSet с начальной емкостью - это создать его с помощью экземпляра класса, такого как List<T>, который реализует ICollection<T>.Он будет вызывать Count при ICollection<T> выделении достаточного пространства для хранения коллекции и добавления всех элементов в HashSet без перераспределения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...