MyClass[] array;
List<MyClass> list;
Каковы сценарии, когда один предпочтительнее другого? И почему?
MyClass[] array;
List<MyClass> list;
Каковы сценарии, когда один предпочтительнее другого? И почему?
Ответы:
На самом деле редко, когда вы захотите использовать массив. Определенно используйте List<T>
любое время, когда вы хотите добавить / удалить данные, так как изменение размеров массивов стоит дорого. Если вы знаете, что данные имеют фиксированную длину, и вы хотите микрооптимизировать по какой-то очень конкретной причине (после сравнительного анализа), тогда может пригодиться массив.
List<T>
предлагает намного больше функциональности, чем массив (хотя LINQ немного выравнивает его), и почти всегда является правильным выбором. За исключением params
аргументов, конечно. ;-п
Как счетчик - List<T>
одномерен; где-как у вас есть прямоугольные (и т. д.) массивы, такие как int[,]
или string[,,]
- но есть другие способы моделирования таких данных (если вам нужно) в объектной модели.
Смотрите также:
Тем не менее, я много использую массивы в своем проекте protobuf-net ; полностью для производительности:
byte[]
он очень важен для кодирования;byte[]
буферный буфер, который я заполняю перед отправкой вниз в основной поток (и vv); быстрее чем и BufferedStream
т.д .;Foo[]
а не List<Foo>
), так как размер устанавливается после сборки и должен быть очень быстрым.Но это определенно исключение; для общей бизнес-обработки, List<T>
выигрывает каждый раз.
На самом деле, просто отвечая на добавление ссылки, о которой я удивлен, пока не упоминалось: запись Эрика Липперта в блоге «Массивы считаются несколько вредными».
Из названия можно судить, что он предлагает использовать коллекции везде, где это практически возможно, но, как справедливо отмечает Марк, есть много мест, где массив действительно является единственным практическим решением.
Несмотря на рекомендации других ответов List<T>
, вы захотите использовать массивы при обработке:
List<T>
здесь, а не байтового массива?
Если вы действительно не заинтересованы в производительности, и под этим я подразумеваю: «Почему вы используете .Net вместо C ++?» Вы должны придерживаться списка <>. Это проще в обслуживании и выполняет всю грязную работу по изменению размера массива за кулисами для вас. (При необходимости List <> довольно умно подходит к выбору размеров массива, поэтому обычно в этом нет необходимости.)
Массивы следует использовать предпочтительнее List, когда неизменность самой коллекции является частью контракта между кодом клиента и провайдера (необязательно неизменность элементов в коллекции) И когда IEnumerable не подходит.
Например,
var str = "This is a string";
var strChars = str.ToCharArray(); // returns array
Ясно, что модификация "strChars" не будет изменять исходный объект "str", независимо от уровня знаний о реализации базового типа "str".
Но предположим, что
var str = "This is a string";
var strChars = str.ToCharList(); // returns List<char>
strChars.Insert(0, 'X');
В этом случае не ясно только из этого фрагмента кода, будет ли метод вставки изменять или не изменять исходный объект "str". Требуется знание уровня реализации String, чтобы сделать это определение, что нарушает подход «Проектирование по контракту». В случае с String это не имеет большого значения, но может иметь большое значение практически во всех остальных случаях. Установка списка только для чтения помогает, но приводит к ошибкам во время выполнения, а не во время компиляции.
To
, собирается создать объект, который не имеет возможности изменять исходный экземпляр, в отличие от strChars as char[]
которого, если действительный, предполагает, что вы теперь можете изменить исходный объект.
str
внутренне использует массив и ToCharArray
возвращает ссылку на этот массив, клиент может str
изменить его, изменив элементы этого массива, даже если размер остается фиксированным. Тем не менее, вы пишете «Понятно, что модификация« strChars »не будет изменять исходный объект« str »». Что мне здесь не хватает? Из того, что я вижу, в любом случае клиент может иметь доступ к внутреннему представлению, и, независимо от типа, это допускает мутации какого-либо рода.
Если я точно знаю , сколько элементов я буду нуждаться, скажем , мне нужно 5 элементов , и только когда - либо 5 элементов , то я использую массив. В противном случае я просто использую список <T>.
В большинстве случаев List
достаточно использования. A List
использует внутренний массив для обработки своих данных и автоматически изменяет размер массива при добавлении большего количества элементов, List
чем его текущая емкость, что делает его более простым в использовании, чем массив, для которого вам нужно заранее знать емкость.
См. Http://msdn.microsoft.com/en-us/library/ms379570(v=vs.80).aspx#datastructures20_1_topic5 для получения дополнительной информации о списках в C # или просто для декомпиляции System.Collections.Generic.List<T>
.
Если вам нужны многомерные данные (например, с использованием матрицы или в графическом программировании), вы, вероятно, предпочтете использовать array
вместо них.
Как всегда, если память или производительность - проблема, измерьте это! В противном случае вы могли бы сделать ложные предположения о коде.
Массивы Vs. Списки - это классическая проблема обслуживания и производительности. Практическое правило, которому следуют почти все разработчики, заключается в том, что вы должны использовать оба подхода, но когда они вступают в конфликт, выбирайте удобство обслуживания вместо производительности. Исключением из этого правила является ситуация, когда производительность уже оказалась проблемой. Если вы несете этот принцип в Arrays Vs. Списки, то, что вы получите, это:
Используйте строго типизированные списки, пока не столкнетесь с проблемами производительности. Если вы столкнулись с проблемой производительности, примите решение о том, принесет ли выпадение в массивы ваше решение с производительностью больше, чем это нанесет ущерб вашему решению с точки зрения обслуживания.
Еще одна ситуация, которая еще не упомянута, - это когда у одного будет большое количество элементов, каждый из которых состоит из фиксированной группы связанных, но независимых переменных, слипшихся воедино (например, координаты точки или вершины трехмерного треугольника). Массив структур с открытыми полями позволит эффективно модифицировать его элементы «на месте», что невозможно для любого другого типа коллекции. Поскольку массив структур хранит свои элементы последовательно в ОЗУ, последовательный доступ к элементам массива может быть очень быстрым. В ситуациях, когда код должен будет выполнить много последовательных проходов через массив, массив структур может превзойти массив или другую коллекцию ссылок на объекты класса в 2: 1; дальше,
Хотя размеры массивов не изменяются, нетрудно заставить код хранить ссылку на массив вместе с количеством используемых элементов, и при необходимости заменить массив на больший. В качестве альтернативы, можно легко написать код для типа, который ведет себя очень похоже на, List<T>
но предоставляет доступ к его резервному хранилищу, что позволяет сказать либо MyPoints.Add(nextPoint);
или, либо MyPoints.Items[23].X += 5;
. Обратите внимание, что последний не обязательно вызовет исключение, если код попытается получить доступ за пределами списка, но в противном случае использование будет концептуально очень похоже на List<T>
.
Point[] arr;
, код может сказать, например arr[3].x+=q;
. Используя, например List<Point> list
, было бы необходимо вместо этого сказать Point temp=list[3]; temp.x+=q; list[3]=temp;
. Было бы полезно, если List<T>
бы был метод Update<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params)
. и компиляторы могут превратиться list[3].x+=q;
в , {list.Update(3, (ref int value, ref int param)=>value+=param, ref q);
но нет такой функции не существует.
list[0].X += 3;
добавит 3 к свойству X первого элемента списка. И list
является List<Point>
и Point
является классом со свойствами X и Y
Списки в .NET являются обертками над массивами и используют массив внутри. Временная сложность операций со списками такая же, как и в случае с массивами, однако есть некоторые дополнительные издержки со всеми дополнительными функциями / простотой использования списков (таких как автоматическое изменение размера и методы, которые идут с классом списка). Я бы рекомендовал использовать списки во всех случаях, если нет веской причины не делать этого, например, если вам нужно написать чрезвычайно оптимизированный код или вы работаете с другим кодом, построенным на массивах.
Вместо того, чтобы сравнивать возможности каждого типа данных, я думаю, что наиболее прагматичный ответ: «Вероятно, различия не так важны для достижения ваших IEnumerable
целей , тем более что они оба реализуют , поэтому следуйте популярному соглашению и используйте List
пока у вас нет причин не делать этого, и в этот момент у вас, вероятно, будет причина использовать массив надList
. "
В большинстве случаев в управляемом коде вы предпочитаете, чтобы с коллекциями было как можно проще работать, а не беспокоиться о микрооптимизации.
Они могут быть непопулярны, но я фанат Arrays в игровых проектах. - Скорость итерации может быть важна в некоторых случаях, потому что foreach на массиве имеет значительно меньшие накладные расходы, если вы не делаете много для каждого элемента - Добавление и удаление не так сложны с вспомогательными функциями - Это медленнее, но в случаях, когда вы строите его только один раз это может не иметь значения - В большинстве случаев меньше дополнительной памяти тратится впустую (действительно очень важно для массивов структур) - Немного меньше мусора и указателей и погоня за указателями
При этом я использую List гораздо чаще, чем Arrays на практике, но у каждого из них есть свое место.
Было бы неплохо, если бы в List был встроенный тип, чтобы они могли оптимизировать накладные расходы и издержки перечисления.
Заполнить список проще, чем массив. Для массивов вам нужно знать точную длину данных, но для списков размер данных может быть любым. И вы можете преобразовать список в массив.
List<URLDTO> urls = new List<URLDTO>();
urls.Add(new URLDTO() {
key = "wiki",
url = "https://...",
});
urls.Add(new URLDTO()
{
key = "url",
url = "http://...",
});
urls.Add(new URLDTO()
{
key = "dir",
url = "https://...",
});
// convert a list into an array: URLDTO[]
return urls.ToArray();
Поскольку никто не упоминает: в C # массив представляет собой список. MyClass[]
и List<MyClass>
оба реализуют IList<MyClass>
. (например, void Foo(IList<int> foo)
можно назвать как Foo(new[] { 1, 2, 3 })
илиFoo(new List<int> { 1, 2, 3 })
)
Итак, если вы пишете метод, который принимает a List<MyClass>
в качестве аргумента, но использует только подмножество функций, вы можете объявить какIList<MyClass>
вместо этого для удобства вызывающих.
Подробности:
List
, он только реализует IList
интерфейс.
Это полностью зависит от условий, в которых требуется структура данных. Например, если вы создаете элементы, которые будут использоваться другими функциями или службами, с помощью List - это идеальный способ сделать это.
Теперь, если у вас есть список элементов, и вы просто хотите отобразить их, скажем, в массиве веб-страниц есть контейнер, который вам нужно использовать.
IEnumerable<T>
- тогда я могу передавать объекты в потоковом режиме, а не буферизировать их.
Стоит упомянуть о способности делать кастинг на месте.
interface IWork { }
class Foo : IWork { }
void Test( )
{
List<Foo> bb = new List<Foo>( );
// Error: CS0029 Cannot implicitly convert type 'System.Collections.Generic.List<Foo>' to 'System.Collections.Generic.List<IWork>'
List<IWork> cc = bb;
Foo[] bbb = new Foo[4];
// Fine
IWork[] ccc = bbb;
}
Поэтому Array предлагает немного больше гибкости при использовании в типе возвращаемого значения или аргументе для функций.
IWork[] GetAllWorks( )
{
List<Foo> fooWorks = new List<Foo>( );
return fooWorks.ToArray( ); // Fine
}
void ExecuteWorks( IWork[] works ) { } // Also accept Foo[]