SortedList <>, SortedDictionary <> та Dictionary <>


98

Я знаходжу це SortedList<TKey, TValue> SortedDictionary<TKey, TValue>і Dictionary<TKey, TValue>реалізую однакові інтерфейси.

  1. Коли слід обирати SortedListі SortedDictionaryбільше Dictionary?
  2. У чому різниця між застосуванням SortedListта SortedDictionaryтерміном його застосування?

Відповіді:


101
  1. Під час ітерації над елементами в будь-якому з цих двох елементів сортуватиметься. Не так з Dictionary<T,V>.

  2. MSDN вирішує різницю між SortedList<T,V>і SortedDictionary<T,V>:

Узагальнений клас SortedDictionary (TKey, TValue) - це двійкове дерево пошуку із O (log n) пошуком, де n - кількість елементів у словнику. У цьому відношенні він подібний до загального класу SortedList (TKey, TValue). Два класи мають схожі об'єктні моделі, і обидва мають O (log n) отримання. Де два класи відрізняються між собою використанням пам'яті та швидкістю вставки та видалення:

SortedList (TKey, TValue) використовує менше пам'яті, ніж SortedDictionary (TKey, TValue).

SortedDictionary (TKey, TValue) має швидші операції вставки та видалення невідсортованих даних: O (log n) на відміну від O (n) для SortedList (TKey, TValue).

Якщо список заповнюється відразу з відсортованих даних, SortedList (TKey, TValue) швидше, ніж SortedDictionary (TKey, TValue).


21
Ще одна практична відмінність полягає в тому, що SortedListви можете отримувати за індексом (на відміну від пошуку за ключем), а в іншому - SortedDictionaryні.
Андрій Савіних

65

введіть тут опис зображення

Я б згадав різницю між словниками.

Наведене зображення показує, що Dictionary<K,V>воно рівне або швидше у кожному випадку, ніж Sortedаналогове, але якщо потрібен порядок елементів, наприклад, щоб їх надрукувати, Sortedвибирається один.

Src: http://people.cs.aau.dk/~normark/oop-csharp/html/notes/collections-note-time-complexity-dictionaries.html


1
Відмінний огляд. Хоча це не в оригінальному питанні, слід зазначити, що якщо ви вибираєте між Immutableверсіями цих словників, то Sortedверсії часто насправді швидші приблизно на 40-50%, ніж невідсортовані аналоги (все-таки O(log(n)), але помітно швидше за операцію) . Однак терміни можуть відрізнятися залежно від того, наскільки впорядковано вхідні дані. Дивіться stackoverflow.com/a/30638592/111575
Авель,

21

Підсумовуючи результати тесту продуктивності - SortedList vs SortedDictionary vs. Dictionary vs Hashtable , результати від найкращого до гіршого для різних сценаріїв:

Використання пам'яті:

SortedList<T,T>
Hashtable
SortedDictionary<T,T>
Dictionary<T,T>

Вставки:

Dictionary<T,T>
Hashtable
SortedDictionary<T,T>
SortedList<T,T>

Пошукові операції:

Hashtable
Dictionary<T,T>
SortedList<T,T>
SortedDictionary<T,T>

операції циклу foreach

SortedList<T,T>
Dictionary<T,T>
Hashtable
SortedDictionary<T,T>

1
При розгляді результатів тестування, можна сумніву сенс існування в SortedDictionary.
beawolf

1
Якщо вам Collectionпотрібно бути, sortedтоді ви можете забути про Hashtableі Dictionary: якщо ви заповнюєте свою колекцію одним пострілом -> перейдіть до SortedList, але якщо ви передбачаєте, що вам часто знадобляться елементи .Addта .Remove-> перейдіть до SortedDictionary.
Ама

Можливо, потрібно пояснити, що sortedозначає: коли ви робите For Each MyItem in Collectionзамість того, щоб обробляти їх у тому порядку, в якому ви спочатку .Addредагували елементи, a sorted Collectionобробляє їх у порядку, відповідно до критеронів на Keyзначеннях (визначених у IComparer). Наприклад, якщо ваші ключі є рядками, то ваша колекція за замовчуванням оброблятиметься в алфавітному порядку ваших ключів, але ви завжди можете визначити власне правило сортування.
Ама

9
  1. Якщо ви хочете, щоб колекцію було відсортовано за ключем, коли ви перебираєте її. Якщо вам не потрібно сортувати ваші дані, вам краще мати лише словник, він матиме кращу продуктивність.

  2. SortedList та SortedDictionary в основному роблять одне і те ж, але реалізуються по-різному, отже, тут пояснюються різні сильні та слабкі сторони .


8

Я бачу, що пропоновані відповіді зосереджені на результатах. У статті, поданій нижче, не міститься нічого нового щодо продуктивності, але вона пояснює основні механізми. Також зверніть увагу, що він не фокусується на трьох CollectionТипах, згаданих у питанні, а звертається до всіх Типів System.Collections.Genericпростору імен.

http://geekswithblogs.net/BlackRabbitCoder/archive/2011/06/16/c.net-fundamentals-choose-the-right-collection-class.aspx

Екстракти:

Словник <>

Словник - це, мабуть, найбільш вживаний асоціативний клас контейнера. Словник - це найшвидший клас для асоціативних пошуків / вставок / видалень, оскільки він використовує хеш-таблицю під обкладинками . Оскільки ключі хешовані, тип ключів повинен правильно реалізовувати GetHashCode () та Equals () належним чином, або ви повинні надати зовнішній IEqualityComparer до словника при побудові. Час вставки / видалення / пошуку елементів у словнику амортизується постійним часом - O (1) - це означає, що незалежно від того, наскільки великий стає словник, час, необхідний для пошуку чогось, залишається відносно постійним. Це дуже бажано для високошвидкісних пошуків. Єдиним недоліком є те, що словник за характером використання хеш-таблиці є невпорядкованим, томуВи не можете легко прокрутити елементи у Словнику по порядку .

Сортований словник <>

Сортований словник схожий на Словник у використанні, але дуже різний у реалізації. SortedDictionary використовує бінарне дерево під ковдрою , щоб зберегти деталі в порядку ключа . Як наслідок сортування, тип, який використовується для ключа, повинен правильно реалізовувати IComparable, щоб ключі могли бути правильно відсортовані. Відсортований словник торгує трохи часу пошуку для можливості підтримувати елементи в порядку, отже, час вставлення / видалення / пошуку у відсортованому словнику є логарифмічним - O (журнал n). Взагалі кажучи, з логарифмічним часом ви можете подвоїти розмір колекції, і для пошуку предмета потрібно виконати лише одне додаткове порівняння. Використовуйте Сортований словник, коли ви хочете швидко шукати, але також хочете мати можливість підтримувати колекцію в порядку за ключем.

SortedList <>

SortedList - це інший відсортований асоціативний клас контейнерів у загальних контейнерах. Ще раз SortedList, як і SortedDictionary, використовує ключ для сортування пар ключ-значення . Однак, на відміну від SortedDictionary, елементи у SortedList зберігаються як відсортований масив елементів. Це означає, що вставки та видалення є лінійними - O (n) - оскільки видалення або додавання елемента може включати зміщення всіх елементів вгору або вниз у списку. Однак час пошуку - O (журнал n), оскільки SortedList може використовувати двійковий пошук, щоб знайти будь-який елемент у списку за його ключем. То чому б вам колись захотілося це зробити? Ну, відповідь полягає в тому, що якщо ви збираєтеся завантажувати SortedList заздалегідь, вставки будуть повільнішими, але оскільки індексація масиву відбувається швидше, ніж наступні посилання на об’єкти, пошук виконується незначно швидше, ніж SortedDictionary. Ще раз я б використовував це в ситуаціях, коли ви хочете швидко шукати і хочете підтримувати колекцію в порядку за ключем, і коли вставки та видалення трапляються рідко.


Орієнтовний огляд основних процедур

Відгуки дуже вітаються, оскільки я впевнений, що я не все зрозумів.

  • Усі масиви мають розмір n.
  • Невідсортований масив = .Add / .Remove - O (1), але .Item (i) - O (n).
  • Відсортований масив = .Add / .Remove - O (n), але .Item (i) - O (log n).

Словник

Пам'ять

KeyArray(n) -> non-sorted array<pointer>
ItemArray(n) -> non-sorted array<pointer>
HashArray(n) -> sorted array<hashvalue>

Додайте

  1. Додати HashArray(n) = Key.GetHash# O (1)
  2. Додати KeyArray(n) = PointerToKey# O (1)
  3. Додати ItemArray(n) = PointerToItem# O (1)

Видалити

  1. For i = 0 to n, знайти iде HashArray(i) = Key.GetHash # O (журнал n) (відсортований масив)
  2. Видалити HashArray(i)# O (n) (відсортований масив)
  3. Видалити KeyArray(i)# O (1)
  4. Видалити ItemArray(i)# O (1)

Отримати товар

  1. For i = 0 to n, знайти iде HashArray(i) = Key.GetHash# O (журнал n) (відсортований масив)
  2. Повернення ItemArray(i)

Петля наскрізь

  1. For i = 0 to n, повернення ItemArray(i)

Сортований словник

Пам'ять

KeyArray(n) = non-sorted array<pointer>
ItemArray(n) = non-sorted array<pointer>
OrderArray(n) = sorted array<pointer>

Додайте

  1. Додати KeyArray(n) = PointerToKey# O (1)
  2. Додати ItemArray(n) = PointerToItem# O (1)
  3. For i = 0 to n, знайти iде KeyArray(i-1) < Key < KeyArray(i)(використовуючи ICompare) # O (n)
  4. Додати OrderArray(i) = n# O (n) (відсортований масив)

Видалити

  1. For i = 0 to n, знайти iде KeyArray(i).GetHash = Key.GetHash# O (n)
  2. Видалити KeyArray(SortArray(i))# O (n)
  3. Видалити ItemArray(SortArray(i))# O (n)
  4. Видалити OrderArray(i)# O (n) (відсортований масив)

Отримати товар

  1. For i = 0 to n, знайти iде KeyArray(i).GetHash = Key.GetHash# O (n)
  2. Повернення ItemArray(i)

Петля наскрізь

  1. For i = 0 to n, повернення ItemArray(OrderArray(i))

SortedList

Пам'ять

KeyArray(n) = sorted array<pointer>
ItemArray(n) = sorted array<pointer>

Додайте

  1. For i = 0 to n, знайти iде KeyArray(i-1) < Key < KeyArray(i)(використовуючи ICompare) # O (log n)
  2. Додати KeyArray(i) = PointerToKey# O (n)
  3. Додати ItemArray(i) = PointerToItem# O (n)

Видалити

  1. For i = 0 to n, знайти iде KeyArray(i).GetHash = Key.GetHash# O (log n)
  2. Видалити KeyArray(i)# O (n)
  3. Видалити ItemArray(i)# O (n)

Отримати товар

  1. For i = 0 to n, знайти iде KeyArray(i).GetHash = Key.GetHash# O (log n)
  2. Повернення ItemArray(i)

Петля наскрізь

  1. For i = 0 to n, повернення ItemArray(i)

0

Намагаючись присвоїти оцінку ефективності кожному випадку, представленому @Lev, я використав наступні значення:

  • O (1) = 3
  • O (log n) = 2
  • O (n) = 1
  • O (1) або O (n) = 2
  • O (log n) або O (n) = 1,5

Результати (вище = краще):

Dictionary:       12.0 
SortedDictionary:  9.0 
SortedList:        6.5

Звичайно, кожен варіант використання надасть більшої ваги певним операціям.


1
Як правило, вага O (log n) буде log (n) / log (2) (+1 кожен раз, коли n подвоюється), тоді як вага O (n) буде n. Отже, ваш зважувальний коефіцієнт буде правильним для розмірів до 4. Будь-що поза ним побачить, що ваше співвідношення 2: 1 швидко зросте. Наприклад, якщо n = 100, то у вас має бути O (log n) = 15. Слідуючи подібному мисленню, ваш O (1) буде важити 100. Висновок: O (n) програє битву досить швидко. Якщо це не так, це означає, що ваш масив малий, і тоді ефективність не турбує.
Ама
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.