Структури даних .NET: ArrayList, List, HashTable, Dictionary, SortedList, SortedDictionary - Швидкість, пам’ять та коли їх використовувати?


213

.NET має багато складних структур даних. На жаль, деякі з них досить схожі, і я не завжди впевнений, коли використовувати один і коли використовувати інший. Більшість моїх книг C # та Visual Basic певною мірою розповідають про них, але вони ніколи не вникають у реальну деталь.

Яка різниця між Array, ArrayList, List, Hashtable, Dictionary, SortedList і SortedDictionary?

Які з них перелічені (IList - чи можна робити "передбачення" циклів)? Які використовують пари ключ / значення (IDict)?

Як щодо сліду пам’яті? Швидкість вставки? Швидкість пошуку?

Чи є якісь інші структури даних, які варто згадати?

Я все ще шукаю більш детальну інформацію про використання пам'яті та швидкість (нотація Big-O).


12
Ви повинні розірвати це питання. Ви запитуєте двадцять різних речей, на половину з яких простий пошук у Google може відповісти. Будь ласка, будьте більш конкретними; важко допомогти, коли ваше питання настільки розсіяне.

33
Я думав про те, щоб її розбити, але зрозумів, що хтось, ймовірно, зможе об'єднати всі ці відповіді в одне місце. Насправді, якщо хтось може придумати таблицю, де все деталізується, це може стати чудовим ресурсом на цьому сайті.
Кренцель

9
Чи можна це питання перетворити на вікі?
BozoJoe

1
Ця стаття MSDN охоплює багато з цих питань, включаючи дерева, графіки та набори, обширне вивчення структур даних
Райан Фішер

1
Райан, статті за цим посиланням 14 років, (12 на момент публікації). Бічна примітка Я останній тиждень читав їх сам. але вони також не включають новіші технології і відчайдушно потребують оновлення. І більше показників та прикладів ефективності.
htm11h

Відповіді:


156

Вгорі голови:

  • Array* - являє собою масив пам'яті старої школи - такий, як псевдонім для звичайного type[]масиву. Можна перерахувати. Неможливо рости автоматично. Я б припустив дуже швидку вставку та швидкість вилучення.

  • ArrayList- автоматично зростаючий масив. Додає більше накладних витрат. Може перераховувати. Мабуть повільніше, ніж звичайний масив, але все ще досить швидкий. Вони багато використовуються в .NET

  • List- один з моїх Favs - може використовуватися з дженериків, так що ви можете мати строго типізований масив, наприклад List<string>. Крім цього, діє дуже схожеArrayList

  • Hashtable- звичайний старий хештель. O (1) до O (n) найгірший випадок. Можна перераховувати значення та властивості ключів, а також робити парами key / val

  • Dictionary - те саме, що вище, тільки сильно набране за допомогою дженериків, таких як Dictionary<string, string>

  • SortedList- відсортований загальний список. Уповільнена вставка, оскільки вона повинна з'ясувати, куди подіти речі. Можна перерахувати, ймовірно, те саме при пошуку, оскільки не потрібно вдаватися, але видалення буде повільніше, ніж звичайний старий список.

Я схильний використовувати Listі Dictionaryвесь час - як тільки ви почнете використовувати їх із сильно набраними дженеріками, повернутися до стандартних негенеріальних справді важко.

Існує також багато інших структур даних - є KeyValuePairякі ви можете використовувати, щоб зробити якісь цікаві речі, є і SortedDictionaryякі можуть бути корисними.


3
Таблиця хешу - це O (1), найгірший випадок (при зіткненнях) може бути O (n)
Джастін Бозоньє

7
Тут є багато інших структур даних, які потрібно додати. як LinkedList, пропуск списку, стек, черга, купа, дерева, графіки. Це також дуже важливі структури даних.
DarthVader

2
ConcurrentDictionary доданий у .Net 4.0 надає загальний словник із безпекою теми
Harindaka

2
Також BlockingCollection <T> забезпечує безпечну реалізацію виробника / споживача
Harindaka

7
ArrayListвикористовує віртуальні методи, але List<T>ні. ArrayListзначною мірою замінено List<T>на стандартні колекції та Collection<T>як базовий клас для користувацьких колекцій. Hashtableбула значною мірою замінена на Dictionary<TKey, TValue>. Я рекомендую уникати ArrayListі Hashtableдля нового коду.
Сем Харвелл

29

Якщо це взагалі можливо, використовуйте дженерики. Це включає:

  • Список замість ArrayList
  • Словник замість HashTable

24

По-перше, всі колекції в .NET реалізують IEnumerable.

По-друге, багато колекцій - це дублікати, оскільки дженерики були додані у версії 2.0 фреймворку.

Отже, хоча загальні колекції, ймовірно, додають функції, здебільшого:

  • Список - це загальна реалізація ArrayList.
  • Словник - це загальна реалізація Hashtable

Масиви - це колекція фіксованого розміру, за допомогою якої ви можете змінити значення, збережене в заданому індексі.

SortedDictionary - це Ідентифікатор, який сортується на основі клавіш. SortedList - це Ідентифікатор, який сортується на основі необхідного IComparer.

Отже, реалізація IDictionary (ті, що підтримують KeyValuePairs): * Hashtable * Словник * SortedList * SortedDictionary

Ще одна колекція, яка була додана в .NET 3.5, - Hashset. Це колекція, яка підтримує задані операції.

Крім того, LinkedList - це стандартна реалізація зв'язаного списку (Список - це список масивів для швидшого пошуку).


20

Ось кілька загальних порад для вас:

  • Ви можете використовувати foreachтипи, які реалізують IEnumerable. IListпо суті є властивостями IEnumberableз Countі Item(доступ до елементів за допомогою нульового індексу). IDictionaryз іншого боку, означає, що ви можете отримати доступ до елементів за допомогою будь-якого індексувального індексу

  • Array, ArrayListі Listвсе здійснити IList. Dictionary, SortedDictionaryта Hashtableвпроваджувати IDictionary.

  • Якщо ви використовуєте .NET 2.0 або новішої версії, рекомендується використовувати загальні аналоги згаданих типів.

  • Для часової та просторової складності різних операцій щодо цих типів слід ознайомитися з їх документацією.

  • Структури даних .NET знаходяться в System.Collectionsпросторі імен. Існують бібліотеки типів, такі як PowerCollections, які пропонують додаткові структури даних.

  • Щоб отримати глибоке розуміння структур даних, зверніться до таких ресурсів, як CLRS .


1
від msdn , схоже, відсортований список впроваджувати IDictionnary - не IList
Хаїм Бенданан

Виправлено. дякую за коментар Схоже, SortedList зберігає список ключів / значень, тому він в основному представляє дані словника. Не пам’ятаєте, як працював цей клас, коли я вперше написав відповідь ...
чорнобрив

9

Структури даних .NET:

Більше до розмови про те, чому ArrayList та List насправді різні

Масиви

Як стверджує один користувач, масиви - це колекція "старої школи" (так, масиви вважаються колекцією, хоча не є частиною System.Collections). Але що таке "old school" щодо масивів порівняно з іншими колекціями, тобто тими, які ви вказали у своєму заголовку (тут, ArrayList та List (Of T))? Почнемо з основ, переглянувши масиви.

Для початку Arrays в Microsoft .NET - це "механізми, які дозволяють розглядати кілька [логічно пов'язаних] елементів як єдину колекцію" (див. Пов'язану статтю). Що це означає? Масиви зберігають окремі елементи (елементи) послідовно, один за одним, в пам'яті зі стартовою адресою. Використовуючи масив, ми можемо легко отримати доступ до послідовно збережених елементів, що починаються з цієї адреси.

Крім цього і всупереч програмуванню 101 загальної концепції, масиви дійсно можуть бути досить складними:

Масиви можуть бути одномірними, багатовимірними або нежирними (про нечіткі масиви варто прочитати). Самі масиви не є динамічними: після ініціалізації масив n розмірів залишає достатньо місця, щоб вмістити n кількість об'єктів. Кількість елементів у масиві не може зростати або зменшуватися. Dim _array As Int32() = New Int32(100)залишає достатньо місця на блоці пам'яті для масиву, який містить 100 об'єктів примітивного типу Int32 (у цьому випадку масив ініціалізується, щоб містити 0s). Адреса цього блоку повертається до _array.

Згідно зі статтею, загальна мовна специфікація (CLS) вимагає, щоб усі масиви були нульовими. Масиви в .NET підтримують масиви на основі нуля; однак це рідше. У результаті "спільної" нульових масивів Microsoft витрачає багато часу на оптимізацію їх продуктивності ; отже, одномірні, нульові масиви (SZ) - це "особливі" - і справді найкраща реалізація масиву (на відміну від багатовимірного тощо) - тому що СЗ мають специфічні мовні інструкції щодо маніпулювання ними.

Масиви завжди передаються за посиланням (як адреса пам'яті) - важливий фрагмент головоломки масиву, який потрібно знати. Хоча вони перевіряють межі (викличе помилку), перевірка меж також може бути відключена на масивах.

Знову ж таки, найбільша перешкода для масивів - це те, що вони не підлягають повторному зміненню. Вони мають "фіксовану" ємність. Представляємо ArrayList та List (Of T) до нашої історії:

ArrayList - негенеричний список

ArrayList (поряд з List(Of T)- хоча є деякі критичні відмінності, тут, пояснено пізніше) - це , можливо , краще за все розглядати як чергове доповнення до колекції (в широкому сенсі). ArrayList успадковує інтерфейс IList (нащадок інтерфейсу 'ICollection'). Самі ArrayLists є об'ємнішими - вимагають більше накладних витрат, ніж списки.

IListдає можливість реалізації трактувати ArrayLists як списки фіксованого розміру (наприклад, масиви); однак, крім додаткової функціональності, доданої ArrayLists, немає реальних переваг у використанні ArrayLists, які мають фіксований розмір як ArrayLists (над масивами) в цьому випадку помітно повільніше.

З мого читання, ArrayLists не можна закреслити: "Використання багатовимірних масивів як елементів ... не підтримується". Знову ще один цвях у труні ArrayLists. ArrayLists також не "надрукував» - це означає , що під ним все, ArrayList просто динамічний масив об'єктів: Object[]. Для цього потрібно багато боксу (неявного) та розпакування (явного) при впровадженні ArrayLists, знову додаючи до їхніх витрат.

Не обгрунтована думка: я думаю, що я пам’ятаю чи читав, чи чув від одного з моїх професорів, що ArrayLists є своєрідною концептуальною дитиною ублюдків спроби переходу з масивів до колекцій списків, тобто, коли колись було велике вдосконалення для масивів, вони більше не є найкращим варіантом, оскільки подальший розвиток було зроблено щодо колекцій

Список (з T): яким став ArrayList (і сподівався бути)

Різниця у використанні пам'яті достатньо значна, коли список (Of Int32) спожив на 56% менше пам'яті, ніж ArrayList, що містить той же примітивний тип (8 Мб проти 19 МБ у вищезгаданій демонстрації джентльмена: знову ж, тут пов'язано ) - хоча це результат, складений 64-бітною машиною. Ця різниця насправді демонструє дві речі: по-перше, (1), об'єкт типу "об'єкт" типу Int32 (ArrayList) є набагато більшим, ніж чистий примітивний тип Int32 (Список); по-друге (2), різниця експоненціальна в результаті внутрішньої роботи 64-бітної машини.

Отже, у чому різниця і що таке Список (Т) ? MSDN визначає List(Of T)як "... сильно набраний список об'єктів, до яких можна отримати доступ за індексом." Тут важливе значення має "сильно набраний" біт: список (з T) "розпізнає" типи і зберігає об'єкти як їх тип. Отже, а Int32зберігається як тип, Int32а не Objectтип. Це усуває проблеми, спричинені боксу та розпакування.

MSDN вказує, що ця різниця вступає в дію лише під час зберігання примітивних типів, а не референтних типів. Занадто, різниця дійсно виникає у великих масштабах: понад 500 елементів. Що ще цікавіше, це те, що документація MSDN говорить: "Для вашої переваги використовувати тип-реалізацію класу List (Of T) замість класу ArrayList ...."

По суті, List (Of T) - це ArrayList, але краще. Це "загальний еквівалент" ArrayList. Як і ArrayList, його не гарантовано сортувати до сортування (перейти до фігури). Список (Of T) також має деяку додаткову функціональність.


5

Я співчуваю питанню - я теж знайшов (знайшов?) Вибір, який дивує вибір, тож я науково поставився зрозуміти, яка структура даних є найшвидшою (я робив тест за допомогою VB, але, думаю, C # був би однаковим, оскільки обидві мови зробіть те саме на рівні CLR). Ви можете побачити деякі результати бенчмаркінгу, проведені мною тут (також є деяке обговорення, який тип даних найкраще використовувати за яких обставин).


3

Вони прописані досить добре в інтелігенції. Просто введіть System.Collections. або System.Collections.Generics (бажано), і ви отримаєте список та короткий опис наявних.


3

Хешшюти / словники - це O (1) продуктивність, тобто виконання не є функцією розміру. Це важливо знати.

EDIT: На практиці середня часова складність для пошуку Hashtable / Dictionary <> пошуку становить O (1).


5
Немає такого поняття, як "вистава". Складність залежить від експлуатації. Наприклад, якщо ви вставите n елементів у словник <>, це не буде O (1) через повторне переосмислення.
Ілля Риженков

2
FYI, навіть при повторному переробці, словник все ще O (1). Розглянемо сценарій перед розширенням словника. Половина елементів - ті, які були додані з моменту останнього розширення - будуть хешировані один раз. Половина решти буде хеширована двічі. Половина решти від цього, три рази тощо. Середня кількість операцій хешування, виконаних на кожному елементі, становитиме 1 + 1/2 + 1/4 + 1/8 ... = 2. Ситуація відразу після розширення по суті однакова, але з кожним елементом, що пройшов хеш, один додатковий час (тому середня кількість хешу - три). Усі інші сценарії між ними.
supercat

3

Загальні колекції будуть ефективнішими, ніж їхні генеричні колеги, особливо, коли повторюється багато предметів. Це тому, що бокс та розпакування більше не відбувається.


2

Важлива примітка про Hashtable vs словник для високочастотної систематичної торгівлі: випуск безпеки потоку

Hashtable - це нитка безпечна для використання декількома нитками. Загальнодоступні статичні учасники словника є безпечними для потоків, але будь-які члени екземплярів не гарантуються таким.

Тож Hashtable залишається "стандартним" вибором у цьому плані.


Частково це правда. Це Hashtableбезпечний для використання лише один автор та декілька читачів одночасно. З іншого боку, це безпечне використання Dictionaryз декількома читачами, доки воно не буде змінено одночасно.
Брайан Менард

Безумовно. Однак у торговому просторі ми одночасно читаємо з даних про поточний ринок та працюючу аналітику, що включає додані записи. Це також залежить від того, скільки торговців використовує систему - якщо це лише ви, це, очевидно, не має значення.
Роб

1
.NET 4.0 надає ConcurrentDictionary <TKey, TValue>
Rob

1

Існують тонкі і не дуже тонкі відмінності між родовими і не родовими колекціями. Вони просто використовують різні основні структури даних. Наприклад, Hashtable гарантує одного-автора-багатьох читачів без синхронізації. Словник не робить.


1

Найпопулярніші C # структури та колекції даних

  • Масив
  • ArrayList
  • Список
  • LinkedList
  • Словник
  • HashSet
  • Стек
  • Черга
  • SortedList

C # .NET має багато різних структур даних, наприклад, одна з найпоширеніших - це масив. Однак C # поставляється із значно більшою кількістю базових структур даних. Вибір правильної структури даних для використання є частиною написання добре структурованої та ефективної програми.

У цій статті я розберуся над вбудованими структурами даних C #, включаючи нові, представлені у C # .NET 3.5. Зауважте, що багато з цих структур даних застосовуються для інших мов програмування.

Масив

Мабуть, найпростіша і найпоширеніша структура даних - це масив. Масив AC # - це в основному список об'єктів. Його визначальними рисами є те, що всі об'єкти одного типу (в більшості випадків) і є їх певна кількість. Характер масиву дозволяє дуже швидко отримувати доступ до елементів на основі їх положення в списку (інакше відомий як індекс). Масив AC # визначається так:

[object type][] myArray = new [object type][number of elements]

Деякі приклади:

 int[] myIntArray = new int[5];
 int[] myIntArray2 = { 0, 1, 2, 3, 4 };

Як видно з наведеного вище прикладу, масив може бути інтіалізований без елементів або з набору існуючих значень. Вставити значення в масив просто, доки вони підходять. Операція стає затратною, коли є більше елементів, ніж розмір масиву, і в цей момент масив потрібно розширити. Це займає більше часу, оскільки всі існуючі елементи потрібно скопіювати в новий, більший масив.

ArrayList

Структура даних C #, ArrayList, - це динамічний масив. Що означає, що ArrayList може містити будь-яку кількість об'єктів і будь-якого типу. Ця структура даних була розроблена для спрощення процесів додавання нових елементів у масив. Під кришкою ArrayList - це масив, розмір якого подвоюється щоразу, коли у нього не вистачає місця. Подвоєння розміру внутрішнього масиву - це дуже ефективна стратегія, яка зменшує кількість копіювання елементів у перспективі. Тут ми не потрапимо в доказ цього. Структура даних дуже проста у використанні:

    ArrayList myArrayList = new ArrayList();
    myArrayList.Add(56);
    myArrayList.Add("String");
    myArrayList.Add(new Form());

Недоліком структури даних ArrayList є повернення отриманих значень у початковий тип:

int arrayListValue = (int)myArrayList[0]

Джерела та додаткову інформацію ви можете знайти тут :


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.