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

213

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

В чем разница между Array, ArrayList, List, Hashtable, Dictionary, SortedList и SortedDictionary?

Какие из них перечислимы (IList - может делать циклы 'foreach')? Какие из них используют пары ключ / значение (IDict)?

Как насчет памяти? Скорость вставки? Скорость поиска?

Есть ли какие-либо другие структуры данных, о которых стоит упомянуть?

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

кренделек
источник
12
Вы должны разбить этот вопрос на части. Вы спрашиваете двадцать разных вещей, половина из которых может ответить на простой поиск в Google. Пожалуйста, будьте более конкретны; трудно помочь, когда твой вопрос так рассеян.
33
Я думал о том, чтобы разбить его, но понял, что кто-то, вероятно, сможет объединить все эти ответы в одном месте. Фактически, если кто-то может придумать таблицу, профилирующую все, это может стать прекрасным ресурсом на этом сайте.
Крендель,
9
Можно ли превратить этот вопрос в вики?
BozoJoe
1
В этой статье MSDN рассматриваются многие из этих вопросов, включая деревья, графики и наборы . Обширный анализ структур данных
Райан Фишер,
1
Райан, статьям по этой ссылке 14 лет (12 на момент публикации). Примечание: я читал их на прошлой неделе сам. но они также не включают новые технологии и остро нуждаются в обновлении. И еще показатели производительности и примеры.
htm11h

Ответы:

156

С верхней части моей головы:

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

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

  • List- один из моих избранных - может использоваться с обобщениями, поэтому вы можете иметь строго типизированный массив, например List<string>. Кроме того, действует очень похожеArrayList

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

  • 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 - это IDictionary, который сортируется на основе ключей. SortedList - это IDictionary, который сортируется на основе требуемого IComparer.

Итак, реализации IDictionary (те, которые поддерживают KeyValuePairs): * Hashtable * Dictionary * SortedList * SortedDictionary

Еще одна коллекция, которая была добавлена ​​в .NET 3.5 - это Hashset. Это коллекция, которая поддерживает операции над множествами.

Кроме того, LinkedList - это стандартная реализация связанного списка (List - это список массивов для более быстрого поиска).

Абе Хайдебрехт
источник
20

Вот несколько общих советов для вас:

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

  • Array, ArrayListИ Listвсе реализовать IList. Dictionary, SortedDictionaryИ Hashtableреализовать IDictionary.

  • Если вы используете .NET 2.0 или выше, рекомендуется использовать универсальные аналоги упомянутых типов.

  • Для временной и пространственной сложности различных операций над этими типами, вы должны обратиться к их документации.

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

  • Чтобы получить полное представление о структурах данных, обратитесь к ресурсам, таким как CLRS .

Крыла
источник
1
из msdn , похоже, что sortedList реализует IDictionnary - не IList
Хаим Бенданан
Исправлена. Спасибо за комментарий. Похоже, SortedList хранит список ключей / значений, поэтому он в основном представляет данные словаря. Не помню, как этот класс работал, когда я впервые написал ответ ...
blackwing
9

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

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

Массивы

Как утверждает один пользователь, массивы являются коллекцией «старой школы» (да, массивы считаются коллекцией, хотя и не являются ее частью System.Collections). Но что такое «старая школа» в отношении массивов по сравнению с другими коллекциями, то есть теми, которые вы перечислили в своем заголовке (здесь ArrayList и List (Of T))? Давайте начнем с основ, посмотрев на массивы.

Начнем с того, что массивы в Microsoft .NET - это «механизмы, позволяющие обрабатывать несколько [логически связанных] элементов как одну коллекцию» (см. Связанную статью). Что это значит? Массивы хранят отдельные элементы (элементы) последовательно, один за другим в памяти с начальным адресом. Используя массив, мы можем легко получить доступ к последовательно сохраненным элементам, начиная с этого адреса.

Помимо этого и вопреки программированию 101 общая концепция, массивы действительно могут быть довольно сложными:

Массивы могут быть одномерными, многомерными или зазубренными (о неровных массивах стоит прочитать). Сами массивы не являются динамическими: после инициализации массив с размером n резервирует достаточно места для хранения n объектов. Количество элементов в массиве не может увеличиваться или уменьшаться. Dim _array As Int32() = New Int32(100)резервирует достаточно места в блоке памяти для массива, чтобы содержать 100 объектов примитивного типа Int32 (в этом случае массив инициализируется, чтобы содержать 0 с). Адрес этого блока возвращается на _array.

Согласно статье, Common Language Specification (CLS) требует, чтобы все массивы начинались с нуля. Массивы в .NET поддерживают ненулевые массивы; однако, это менее распространено. В результате «общности» массивов с нулями Microsoft потратила много времени на оптимизацию их производительности ; следовательно, одномерные массивы, основанные на нулях (SZ), являются «специальными» - и действительно лучшая реализация массива (в отличие от многомерных и т. д.) - потому что у SZ есть специальные инструкции языка-посредника для манипулирования ими.

Массивы всегда передаются по ссылке (как адрес памяти) - важная часть головоломки Массив, которую нужно знать. Хотя они выполняют проверку границ (выдаст ошибку), проверка границ также может быть отключена для массивов.

Опять же, самым большим препятствием для массивов является то, что они не могут быть изменены. Они имеют «фиксированную» емкость. Представляем ArrayList и List (Of T) в нашей истории:

ArrayList - неуниверсальный список

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

IListпозволяет реализации обрабатывать ArrayLists как списки фиксированного размера (например, Arrays); однако, помимо дополнительной функциональности, добавленной ArrayLists, нет никаких реальных преимуществ использования ArrayLists фиксированного размера, поскольку ArrayLists (по сравнению с Arrays) в этом случае заметно медленнее.

Из моего чтения ArrayLists не может быть неровным: «Использование многомерных массивов в качестве элементов ... не поддерживается». Опять еще один гвоздь в гробу ArrayLists. ArrayLists также не «напечатал» - это означает , что под ним все, ArrayList просто динамический массив объектов: Object[]. Это требует много коробок (неявных) и распаковок (явных) при реализации ArrayLists, что снова увеличивает их накладные расходы.

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

List (Of T): каким ArrayList стал (и надеялся)

Разница в использовании памяти достаточно значительна, когда List (Of Int32) потребляет на 56% меньше памяти, чем ArrayList с тем же типом примитива (8 МБ против 19 МБ в приведенной выше демонстрации, связанной с джентльменом: опять же, здесь ) это результат, составленный 64-битной машиной. Это различие действительно демонстрирует две вещи: во-первых (1) «объект» в виде типа Int32 (ArrayList) в штучной упаковке намного больше, чем чистый тип примитива Int32 (List); во-вторых (2), разница является экспоненциальной в результате внутренней работы 64-битной машины.

Итак, в чем разница и что такое List (Of T) ? MSDN определяет List(Of T)как: «... строго типизированный список объектов, к которым можно получить доступ по индексу». Здесь важен бит «строго типизированный»: List (Of T) «распознает» типы и сохраняет объекты как их типы. Таким образом, an Int32хранится как тип, Int32а не как Objectтип. Это устраняет проблемы, вызванные боксом и распаковкой.

MSDN указывает, что это различие вступает в силу только при хранении примитивных типов, а не ссылочных типов. Кроме того, разница действительно возникает в больших масштабах: более 500 элементов. Что еще интереснее, документация MSDN гласит: «В ваших интересах использовать реализацию класса List (Of T) для конкретного типа вместо использования класса ArrayList ....»

По сути, List (Of T) является ArrayList, но лучше. Это «универсальный эквивалент» ArrayList. Как и ArrayList, сортировка не гарантируется, пока не будет отсортирована (см. Рисунок). Список (Of T) также имеет некоторые дополнительные функции.

Томас
источник
5

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

Энди Браун
источник
3

Они написаны довольно хорошо в intellisense. Просто введите System.Collections. или System.Collections.Generics (предпочтительно), и вы получите список и краткое описание того, что доступно.

Джоэл Коухорн
источник
3

Хеш-таблицы / словари имеют производительность O (1), что означает, что производительность не зависит от размера. Это важно знать.

РЕДАКТИРОВАТЬ: На практике средняя сложность времени для поиска Hashtable / Dictionary <> составляет O (1).

Крис
источник
5
Там нет такой вещи, как «производительность». Сложность зависит от операции. Например, если вы вставите n элементов в Dictionary <>, это не будет O (1) из-за перефразирования.
Илья Рыженков
2
К вашему сведению, даже с перефразировкой, словарь по-прежнему O (1). Рассмотрим сценарий непосредственно перед расширением словаря. Половина элементов - те, которые были добавлены с момента последнего расширения - будет хеширована один раз. Половина остатка будет хеширована дважды. Половина остатка от этого, три раза и т. Д. Среднее число операций хеширования, выполненных для каждого элемента, будет 1 + 1/2 + 1/4 + 1/8 ... = 2. Ситуация сразу после раскрытия, по сути, та же самая, но с каждым элементом, который был хэширован один раз (так что среднее число хэшей равно трем). Все остальные сценарии между ними.
суперкат
3

Универсальные коллекции будут работать лучше, чем их неуниверсальные аналоги, особенно при переборе многих элементов. Это потому, что бокс и распаковка больше не происходит.

Русь Кэм
источник
2

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

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

Таким образом, Hashtable остается «стандартным» выбором в этом отношении.

обкрадывать
источник
Это отчасти верно. HashtableЯвляется безопасным для использования только с одним писателем и несколько читателей одновременно. С другой стороны, безопасно использовать Dictionaryнесколько считывателей, если они не изменены одновременно.
Брайан Менард
Определенно. Тем не менее, в области торговли мы одновременно читаем рыночные данные и запускаем аналитику, включающую добавленные записи. Это также зависит от того, сколько трейдеров используют систему - если это только вы, это, очевидно, не имеет значения.
Роб
1
.NET 4.0 предоставляет ConcurrentDictionary <TKey, TValue>
Роб
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]

Источники и дополнительную информацию вы можете найти здесь :

leonidaa
источник