MyClass[] array;
List<MyClass> list;
Які сценарії, коли один є кращим за інший? І чому?
MyClass[] array;
List<MyClass> list;
Які сценарії, коли один є кращим за інший? І чому?
Відповіді:
Насправді дуже рідко ви хочете використовувати масив. Однозначно використовуйте List<T>
будь-який час, коли ви хочете додати / видалити дані, оскільки розмір масивів дорогий. Якщо ви знаєте, що дані фіксованої довжини, і ви хочете мікрооптимізувати з якоїсь дуже конкретної причини (після тестування), то масив може бути корисним.
List<T>
пропонує набагато більше функціональних можливостей, ніж масив (хоча LINQ його трохи вирівнює), і майже завжди є правильним вибором. За винятком params
аргументів, звичайно. ;-p
Як лічильник - List<T>
є одновимірним; де - як у вас є прямокутні (тощо) масиви на зразок int[,]
або string[,,]
- але є інші способи моделювання таких даних (якщо вам потрібно) в об'єктній моделі.
Дивись також:
Це означає, що я багато використовую масиви в своєму проекті протобуф-нетто ; повністю для продуктивності:
byte[]
дуже важливий для кодування;byte[]
буфер, який я заповнюю, перш ніж надсилати вниз до нижнього потоку (і vv); швидше, ніж BufferedStream
тощо;Foo[]
а не List<Foo>
), оскільки розмір фіксується після побудови, і він повинен бути дуже швидким.Але це, безумовно, виняток; для загальної обробки бізнесу, List<T>
виграш кожного разу.
Дійсно, лише відповівши, щоб додати посилання, яке я здивований, ще не було згадано: запис у блозі Еріка Ліпперта на тему "Масиви вважаються дещо шкідливими".
З назви можна судити, що він пропонує використовувати колекції там, де це практично - але, як справедливо зазначає Марк, є багато місць, де масив справді є єдиним практичним рішенням.
Незважаючи на інші відповіді, що рекомендують List<T>
, вам потрібно буде використовувати масиви під час обробки:
List<T>
тут, а не байтового масиву?
Якщо ви дійсно не переймаєтесь продуктивністю, і я маю на увазі: "Для чого ви використовуєте .Net замість C ++?" вам слід дотримуватися списку <>. Це простіше в обслуговуванні та виконує всю брудну роботу щодо зміни розміру масиву за кадром для вас. (Якщо потрібно, Список <> досить розумний щодо вибору розмірів масиву, тому зазвичай не потрібно.)
Масиви слід використовувати в перевазі «Список», коли незмінність самої колекції є частиною договору між кодом клієнта та постачальника (не обов'язково незмінність елементів у колекції) І коли 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>.
У більшості випадків використання a 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. Списки, то ви отримуєте це:
Використовуйте сильно набрані списки, поки не виникнуть проблеми з продуктивністю. Якщо ви зіткнулися з проблемою продуктивності, прийміть рішення, чи випадання масивів принесе користь вашому рішенню з продуктивністю більше, ніж буде шкодою для вашого рішення в плані технічного обслуговування.
Ще одна ситуація, про яку ще не було сказано, - це коли у вас буде велика кількість елементів, кожен з яких складається з фіксованої купи пов'язаних між собою незалежних змінних, скріплених між собою (наприклад, координати точки або вершини 3d трикутника). Масив структур відкритого поля дозволить ефективно змінювати його елементи "на місці" - те, що неможливо з будь-яким іншим типом колекції. Оскільки масив структур утримує свої елементи послідовно в оперативній пам'яті, послідовний доступ до елементів масиву може бути дуже швидким. У ситуаціях, коли коду потрібно буде зробити безліч послідовних проходів через масив, масив структур може перевершити масив або іншу колекцію посилань на об'єкт класу на коефіцієнт 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
".
Більшу частину часу в керованому коді ви хочете віддавати перевагу колекціям як можна простіше працювати з можливістю, турбуючись про мікрооптимізацію.
Вони можуть бути непопулярними, але я прихильник масивів у ігрових проектах. - Швидкість ітерації може бути важливою в деяких випадках, якщо передбачення на масиві має значно менші накладні витрати, якщо ви не робите багато за один елемент - додавання та видалення не так вже й важко за допомогою помічників - це повільніше, але у випадках, коли ви збираєте його лише один раз це може не мати значення - У більшості випадків витрачається менше зайвої пам’яті (лише справді важливо для масивів структур) - трохи менше сміття та покажчиків та погоні за вказівниками
Коли це говориться, я використовую 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 })
)
Отже, якщо ви пишете метод, який приймає 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[]