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

182

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

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

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

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

Можно ли упомянуть другие структуры данных?

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

  • 11
    Вы должны разбить этот вопрос на части. Вы спрашиваете двадцать разных вещей, половина из которых может ответить на простой поиск в Google. Пожалуйста, будьте более конкретны; трудно помочь, когда твой вопрос так рассеян.
  • 29
    Я думал о том, чтобы разбить его, но понял, что кто-то, вероятно, сможет объединить все эти ответы в одном месте. Фактически, если кто-то может придумать таблицу, профилирующую все, это может стать прекрасным ресурсом на этом сайте.
Показать ещё 4 комментария
Теги:
arrays
data-structures

14 ответов

137
Лучший ответ

Сверху моей головы:

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

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

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

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

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

  • SortedList - отсортированный общий список. Замедлялся при вставке, так как он должен выяснить, куда положить вещи. Может перечислить., Возможно, то же самое при поиске, так как оно не нужно прибегать, но удаление будет медленнее обычного старого списка.

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

Также есть много других структур данных - там KeyValuePair, которые вы можете использовать, чтобы сделать некоторые интересные вещи, там может быть полезен SortedDictionary.

  • 3
    Хэш-таблица имеет значение O (1), наихудший случай (при столкновениях) может быть O (n)
  • 5
    Есть много других структур данных, которые вы должны добавить сюда. как LinkedList, Пропустить список, Стек, Очередь, Куча, Деревья, Графики. Это очень важные структуры данных.
Показать ещё 5 комментариев
24

Если это вообще возможно, используйте generics. Это включает в себя:

  • Список вместо ArrayList
  • Словарь вместо HashTable
19

Во-первых, все коллекции .NET реализуют IEnumerable.

Во-вторых, многие коллекции дублируются, потому что в версии 2.0 фреймворка добавлены дженерики.

Итак, хотя общие коллекции скорее всего добавят функции, по большей части:

  • Список представляет собой общую реализацию ArrayList.
  • Словарь - это общая реализация Hashtable

Массивы представляют собой коллекцию фиксированного размера, в которой вы можете изменить значение, хранящееся в данном индексе.

SortedDictionary - это IDictionary, который сортируется на основе ключей. SortedList - это IDictionary, который сортируется на основе требуемого IComparer.

Таким образом, реализации IDictionary (поддерживающие KeyValuePairs): * Хеш-таблица * Словарь * SortedList * SortedDictionary

Другая коллекция, добавленная в .NET 3.5, - это Hashset. Это коллекция, которая поддерживает операции набора.

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

17

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

  • Вы можете использовать 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 , похоже , SortedList реализации IDictionnary - не IList
  • 0
    Исправлена. Спасибо за комментарий. Похоже, SortedList хранит список ключей / значений, поэтому он в основном представляет данные словаря. Не помню, как этот класс работал, когда я впервые написал ответ ...
15

Хороший обманщик, в котором упоминаются сложности для структур данных, алгоритмы и т.д.

  • 0
    Жаль, что я не мог бы поднять это дважды, удивительный офигенный ресурс - не могу поверить, что я никогда не видел это
  • 2
    Это круто, но не совсем точно для этого вопроса. Например, в .NET списки реализованы как динамические массивы, а не как связанные списки, как вы ожидаете. Из-за этого мера сложности, используемая здесь, не обязательно точна.
Показать ещё 1 комментарий
5

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

4

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

Подробнее о разговоре о том, почему ArrayList и List действительно отличаются

Массивы

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

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

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

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

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

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

Опять же, самым большим препятствием для массивов является то, что они не переоцениваются. У них есть "фиксированная" емкость. Представляем ArrayList и List (Of T) нашей истории:

ArrayList - не общий список

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

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

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

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

Список (Of T): какой ArrayList стал (и надеется)

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

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

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

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

3

Общие коллекции будут работать лучше, чем их не общие роды, особенно при повторении множества элементов. Это связано с тем, что бокс и распаковка больше не встречаются.

3

Hashtables/Dictionaries - это производительность O (1), что означает, что производительность не зависит от размера. Это важно знать.

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

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

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

2

На самом деле, я думаю, MSDN помогает дать неплохие ответы на все эти вопросы. Просто просмотрите коллекции .NET.

  • 0
    Предоставление ссылки на Википедию не решает проблему, вы должны предоставить ссылку на оригинальную статью msdn.
1

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

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

Итак, Hashtable остается "стандартным" выбором в этом отношении.

  • 0
    Это отчасти верно. Hashtable безопасен для использования только с одним писателем и несколькими читателями одновременно. С другой стороны, безопасно использовать Dictionary с несколькими читателями, если он не изменяется одновременно.
  • 0
    Определенно. Однако в области торговли мы одновременно читаем данные из реального рынка и запускаем аналитику, которая включает в себя добавленные записи. Это также зависит от того, сколько трейдеров используют систему - если это только вы, это, очевидно, не имеет значения.
Показать ещё 1 комментарий
1

Существуют тонкие и не очень тонкие различия между родовыми и неродственными коллекциями. Они просто используют разные базовые структуры данных. Например, Hashtable гарантирует одночиповое считывание без синхронизации. Словарь не работает.

0

Безопасность потоков может быть достигнута с помощью ConcurrentDictionary. HashTable - не единственный вариант.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню