Почему SortedList <TKey, TValue> не использует указатели для значений?

Итак, я просматривал реализацию SortedList<TKey, TValue> и реализацию Add (который вызывает Insert, показанный ниже) действительно удивил меня.

Метод Add делает очевидный двоичный поиск для определения индекса, в котором должен идти KVP, но Insert кажется, что он может быть значительно улучшен (хотя и в больших масштабах):

private void Insert(int index, TKey key, TValue value)
{
  if (this._size == this.keys.Length)
    this.EnsureCapacity(this._size + 1);
  if (index < this._size)
  {
    Array.Copy((Array) this.keys, index, (Array) this.keys, index + 1, this._size - index);
    Array.Copy((Array) this.values, index, (Array) this.values, index + 1, this._size - index);
  }
  this.keys[index] = key;
  this.values[index] = value;
  ++this._size;
  ++this.version;
}

Если я правильно прочитал это, и я оставляю за собой право быть неправым во все времена, это операция O(2n).

Мне кажется, что значения должны быть реализованы с помощью указателей. Вид вроде LinkedList по отношению к значению из ключа, но не связан с тем, что он не поддерживает произвольный доступ. Более того, ключ просто связан с его значением. Операция get не будет медленнее, и ни один из них не будет удален, потому что у нас есть указатель, но теперь операция добавления будет O(n).

Может кто-то пролить свет на то, почему решение, возможно, пошло в этом направлении?

Ответ 1

Это не должно вас удивлять, это хорошо документировано в статье MSDN для SortedList:

SortedDictionary имеет более быструю операцию вставки и удаления для несортированных данных, O (logn) в отличие от O (n) для SortedList.

SortedDictionary использует красно-черное дерево (т.е. "указатели" ), SortedList - это массив. Вы выбираете между ними в зависимости от того, что вы делаете с коллекцией. Оба являются O (logn) для поиска, но если вы часто повторяете сборку, вы можете опередить SortedList много. Он использует кэши процессора намного эффективнее. Делает огромную разницу на современных машинах.

Также обратите внимание, что эффективность добавления элементов в коллекции сильно зависит от того, как отсортированы элементы. A SortedDictionary действительно любит случайные данные, дает гораздо лучшие шансы не переустанавливать деревья. После сортировки он дает худшее поведение O (n). SortedList действительно любит отсортированные элементы, делает добавление O (1).

Ответ 2

Другим очень важным отличием, помимо скорости/большой сложности O, является нехватка памяти. С деревом (SortedDictionary) накладные расходы имеют величину 50-70 байт на пару ключей/значений (приблизительно приблизительно на дереве F # AVL с элементами 1M <int64,int64> на x64, но с красным/черным должны быть в этом диапазоне), а SortedList занимает всего 2 байта на пару.

Точка, с типами значений, например. <int,int>, SortedDictionary может "растратить" несколько раз больше памяти, чем полезную полезную нагрузку, с единственным изолированным преимуществом более быстрой случайной вставки. На практике преимущество кэша процессора в SortedList настолько заметно (константа в O (lon n)), что нужно измерять разницу для каждого конкретного случая использования (отношение поиска/вставок, шаблонов вставок, требований к памяти/скорости).