Структуры данных .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) также имеет некоторые дополнительные функции.