У чому різниця між списком (Т) та колекцією (Т)?


93

Я бачив, як вони використовуються багато в чому однаково, і я переживаю, що збираюся піти шляхом дизайну, який є незворотним, якщо я цього не зрозумію краще. Крім того, я використовую .NET.

Відповіді:


50

Collection<T>це настроювана обгортка навколо IList<T>. Хоча IList<T>це не герметично, воно не надає жодних точок налаштування. Collection<T>Методи '' за замовчуванням делеговані стандартним IList<T>методам, але їх можна легко замінити, щоб зробити те, що ви хочете. Також можна підключати події всередині aCollection<T> що, на мою думку, неможливо зробити за допомогою IList.

Коротше кажучи, набагато простіше продовжити його після факту, що потенційно може означати набагато менше рефакторингу.


@Marc Gravell, @Adam Lassek: Чи не можна зробити те саме зі списком, приховавши метод InsertItem (...) за допомогою публічної порожнечі new InsertItem (...), а потім викликавши base.InsertItem (.. .) зсередини ? Це все одно не порушить контракт. (у списку назва «Вставити», але тим не менше). То яка велика справа в дотриманні колекцій <T>?
DeeStackOverflow

4
@DeeStackOverflow - тому що метод приховування не використовуватиметься будь-яким кодом , який використовує різні API - тобто все , що виглядає для IList, IList<T>, і List<T>т.д. Коротше кажучи, ви не маєте жодного уявлення , чи буде це назвати. Поліморфізм це виправляє.
Марк Гравелл

@AdamLassek: Можливо, ви захочете додати у своїй відповіді приблизно ObservableCollection<T>як приклад, коли методи замінені, щоб повідомляти про зміни.
Кіран Чалла

84

У C # є три концепції представлення мішка об’єктів. У порядку збільшення функцій вони:

  • Численні - невпорядковані, немодифікуються
  • Колекція - може додавати / видаляти елементи
  • Список - дозволяє елементам мати замовлення (доступ та видалення за індексом)

Численні немає порядку. Ви не можете додавати або видаляти елементи з набору. Ви навіть не можете отримати кількість предметів у наборі. Це суворо дозволяє вам отримати доступ до кожного предмета в наборі, один за одним.

Колекція - це модифікований набір. Ви можете додавати та видаляти об'єкти з набору, ви також можете отримати кількість елементів у наборі. Але порядку все ще немає, а оскільки його немає: немає можливості отримати доступ до елемента за індексом, а також немає способу сортування.

Список - це впорядкований набір об’єктів. Ви можете сортувати список, отримувати доступ до елементів за індексом, видаляти елементи за індексом.

Насправді, розглядаючи інтерфейси для них, вони будують один на одного:

  • interface IEnumerable<T>

    • GetEnumeration<T>
  • interface ICollection<T> : IEnumerable<T>

    • Add
    • Remove
    • Clear
    • Count
  • interface IList<T> = ICollection<T>

    • Insert
    • IndexOf
    • RemoveAt

Оголошуючи змінні або параметри методу, слід вибрати використання

  • I незліченна
  • Яколекція
  • IList

виходячи з концептуального, вам потрібно робити з набором об'єктів.

Якщо вам просто потрібно вміти щось робити з кожним об’єктом у списку, то вам потрібно лише IEnumerable:

void SaveEveryUser(IEnumerable<User> users)
{
    for User u in users
      ...
}

Ви не все одно , якщо користувачі будуть зберігатися в List<T>, Collection<T>, Array<T>або що - небудь ще. Вам потрібен лише IEnumerable<T>інтерфейс.

Якщо вам потрібно вміти додавати, видаляти або підраховувати елементи в наборі, скористайтеся колекцією :

ICollection<User> users = new Collection<User>();
users.Add(new User());

Якщо ви дбаєте про порядок сортування і вам потрібно, щоб порядок був правильним, скористайтеся списком :

IList<User> users = FetchUsers(db);

У формі діаграми:

| Feature                | IEnumerable<T> | ICollection<T> | IList<T> |
|------------------------|----------------|----------------|----------|
| Enumerating items      | X              | X              | X        |
|                        |                |                |          |
| Adding items           |                | X              | X        |
| Removing items         |                | X              | X        |
| Count of items         |                | X              | X        |
|                        |                |                |          |
| Accessing by index     |                |                | X        |
| Removing by indexx     |                |                | X        |
| Getting index of item  |                |                | X        |

List<T>І Collection<T>в System.Collections.Genericдва класи , які реалізують ці інтерфейси; але це не єдині класи:

  • ConcurrentBag<T>є впорядкований мішок предметів ( IEnumerable<T>)
  • LinkedList<T>- це сумка, в якій вам заборонено отримувати доступ до предметів за індексом ( ICollection); але ви можете довільно додавати та вилучати елементи з колекції
  • SynchronizedCollection<T> у впорядкованій колекції, де ви можете додавати / видаляти елементи за індексом

Ви можете легко змінити:

IEnumerable<User> users = new SynchronizedCollection<User>();

SaveEveryUser(users);

tl; д-р

  • Перелічені - елементи доступу, не впорядковані, не модифікуються
  • Колекція - може бути змінена (додавання, видалення, підрахунок)
  • Список - доступ до індексу

Виберіть потрібну вам концепцію , а потім скористайтесь відповідним класом.


11
OP запитав про конкретні типи, і ви порівняли інтерфейси. Конкретний тип Collection <T> реалізує IList <T> і має доступ за допомогою індексу.
JJS

2
Відповідь хороша, але відхилена від питання. Не можу відповісти вашій відповіді для класів Collection <T> та List <T>, я маю на увазі з потенційного питання, якщо я спробую перевірити вашу думку, вони просто не виправдовують. Щодо загальної відповіді, ви можете мати рацію, що колекція не впорядкована, тому немає індексації, але список упорядкований, тому вставка можлива за певним індексом.
Kylo Ren

що, якби я хотів мати функції з ICollection <T>, а також сортувати і знайти можливість?

2
Якщо список упорядкований, а колекція не впорядкована, це буде величезна функціональна різниця, щоб легко навчати нового учня (як я) вибирати. Але почекайте, чому ви кажете, що у колекції немає замовлення? Він надає метод IndexOf () та RemoveAt () , тож він замовлений, чи не так? Я щось тут пропустив?
RayLuo

1
@RayLuo Я маю на увазі спеціально ICollection<T>та IList<T>. Різні конкретні реалізації можуть поводитися по-різному. Наприклад, якщо ви отримуєте доступ до List<T>через його IEnumerable<T>інтерфейс, то у вас немає можливості додавати, видаляти, сортувати або підраховувати елементи у списку.
Ян Бойд

43

List<T>призначений для внутрішнього використання в коді програми. Вам слід уникати написання загальнодоступних API, які приймають або повертають List<T>( натомість використовуйте суперклас або інтерфейс колекції).

Collection<T> обслуговує базовий клас для спеціальних колекцій (хоча його можна використовувати безпосередньо).

Подумайте про використання Collection<T>у своєму коді, якщо немає певних особливостей, List<T>які вам потрібні.

Вищезазначені лише рекомендації.

[Адаптовано з: Керівних принципів проектування, друге видання]


Варто зазначити, що типи, які використовують будь- які змінні об'єкти для інкапсуляції власного стану, повинні уникати повернення об'єктів такого типу, якщо тільки ці об'єкти не мають засобів сповіщення свого власника про їх мутацію або назва методу, що повертає object однозначно означає, що він повертає новий екземпляр. Зверніть увагу, що, наприклад, Dictionary<string, List<string>>повернення a List<string>- це нормально, оскільки стан словника інкапсулює лише ідентифікатори списків, а не їх вміст.
supercat

37

List<T>є дуже часто зустрічається контейнером, оскільки він настільки універсальний (з безліччю зручних методів, як Sort, Findтощо) - але не має точок розширення, якщо ви хочете замінити будь-яку поведінку (наприклад, позначте елементи на вставці).

Collection<T>є обгорткою навколо будь-якого IList<T>(за замовчуванням List<T>) - він має точки розширення ( virtualметоди), але не так багато методів підтримки, як Find. Через опосередкованість він трохи повільніший, ніж List<T>, але не набагато.

З допомогою LINQ, додаткові методи в List<T>стає менш важливим, так як LINQ до об'єктів , як правило , надати їм все одно ... наприклад First(pred), OrderBy(...)і т.д.


6
Колекція <T> не має методу foreach, навіть у Linq-to-Objects.
tuinstoel

7
@tuinstoel - але це тривіально додати.
Марк Гравелл

12

Список швидший.

Зробіть, наприклад

private void button1_Click(object sender, EventArgs e)
{
  Collection<long> c = new Collection<long>();
  Stopwatch s = new Stopwatch();
  s.Start();
  for (long i = 0; i <= 10000000; i++)
  {
    c.Add(i);
  }
  s.Stop();
  MessageBox.Show("collect " + s.ElapsedMilliseconds.ToString());

  List<long> l = new List<long>();
  Stopwatch s2 = new Stopwatch();
  s2.Start();
  for (long i = 0; i <= 10000000; i++)
  {
    l.Add(i);
  }
  s2.Stop();
  MessageBox.Show("lis " + s2.ElapsedMilliseconds.ToString());


}

на моїй машині List<>майже вдвічі швидше.

Редагувати

Я не можу зрозуміти, чому люди голосують проти цього. Як на моїй робочій машині, так і на домашній машині, код <> є швидшим на 80%.


1
Як це швидше? Пошук? Вставка? Видалення? Шукати? Чому це швидше?
Дуг Т.

17
Список
набирає

1
Збір має менше методів. Тому це швидше. QED. (жартую, я не тролюю)
Рей

2
Спробував на моїй машині, і список приблизно на 20% швидший. Було б цікаво обговорити, чому це може бути. Можливо, список кращий з виділенням пам'яті.
Рей

10
Методи списку не успадковуються, тому немає перевірок, чи вони були успадковані; Методи колекції успадковуються. Перевага полягає в тому, що ви можете використовувати колекцію як базовий клас для успадкування та створення власної колекції.
Річард Гадсден,

11

Список представляє колекцію, де порядок предметів важливий. Він також підтримує методи сортування та пошуку. Збір - це більш загальна структура даних, яка робить менше припущень щодо даних, а також підтримує менше методів їх маніпулювання. Якщо ви хочете розкрити власну структуру даних, вам, ймовірно, слід розширити колекцію. Якщо вам потрібно маніпулювати даними без викриття структури даних, список, мабуть, є найбільш зручним способом.


4

Це одне з тих питань міської школи. Колекція Т - це свого роду реферат; може бути реалізація за замовчуванням (я не хлопець .net / c #), але колекція матиме основні операції, такі як додавання, видалення, ітерація тощо.

Список T передбачає деякі особливості щодо цих операцій: додавання має займати постійний час, видалення має займати час, пропорційний кількості елементів, getfirst повинен мати постійний час. Загалом, Список є різновидом Колекції, але Колекція не обов’язково є різновидом Списку.


4

Ганзельман говорить : " Collection<T>схожий на список, і він навіть має List<T>внутрішній характер. КОЖНИЙ єдиний метод делегує внутрішній List<T>. Він включає захищену властивість, яка виставляє" List<T>.

EDIT: Collection<T>не існує у System.Generic.Collections .NET 3.5. Якщо ви переходите з .NET 2.0 на 3.5, вам потрібно буде змінити код, якщо ви використовуєте багато Collection<T>об’єктів, якщо я не пропускаю щось очевидне ...

EDIT 2: Collection<T>тепер знаходиться у просторі імен System.Collections.ObjectModel у .NET 3.5. У файлі довідки сказано:

"Простір імен System.Collections.ObjectModel містить класи, які можна використовувати як колекції в об'єктній моделі багаторазової бібліотеки. Використовуйте ці класи, коли властивості або методи повертають колекції."


4

Усі ці інтерфейси успадковуються IEnumerable, і ви повинні переконатися, що розумієте. Цей інтерфейс в основному дозволяє використовувати клас у операторі foreach (на C #).

  • ICollection- це найпростіший з перерахованих вами інтерфейсів. Це незліченний інтерфейс, який підтримує a, Countі все про це.
  • IListце все, що ICollectionє, але він також підтримує додавання та видалення елементів, отримання елементів за індексом і т. д. Це найбільш часто використовуваний інтерфейс для "списків об'єктів", який я туманно знаю.
  • IQueryableце незліченний інтерфейс, який підтримує LINQ. Ви завжди можете створити IQueryableз IList і використовувати LINQ to Objects, але ви також знайдете, що IQueryableвикористовується для відкладеного виконання операторів SQL у LINQ to SQL та LINQ to Entities.
  • IDictionary- це інша тварина в тому сенсі, що це відображення унікальних ключів до цінностей. Він також перелічений тим, що ви можете перерахувати пари ключ / значення, але в іншому випадку він служить для іншої мети, ніж інші, які ви перерахували

ICollection підтримує додавання / видалення / очищення: msdn.microsoft.com/en-us/library/…
amnesia

4

Відповідно до MSDN, List (Of T) .Add - це "операція O (n)" (коли "Ємність" перевищена), тоді як Collection (Of T) .Add - це завжди "операція O (1)". Це було б зрозуміло, якщо List реалізується за допомогою масиву, а колекція пов’язаного списку. Однак, якби це було так, можна було б очікувати, що Collection (Of T). Елемент буде "операцією O (n)". Але - це - ні !?! Collection (Of T) .Item - це "операція O (1)", як і List (Of T).

На додачу до цього, пост "tuinstoel" "29 грудня 2008 року о 22:31" над тестами швидкості претензій показує Список (З Т). Додайте, щоб він був швидшим за Збір (З Т). Лонга та Струни. Хоча я отримав лише ~ 33% швидше проти заявлених 80%, згідно з MSDN, це мало бути навпаки, і до "n" разів!?!


3

Обидва реалізують однакові інтерфейси, тому вони будуть поводитися однаково. Можливо, вони реалізовані по-різному внутрішньо, але це довелося б перевірити.

Єдині реальні відмінності, які я бачу, - це простори імен та те, що Collection<T>позначено ComVisibleAttribute(false), тому код COM не може його використовувати.


Вони реалізують різні інтерфейси - List <T> реалізує IList <T>, де Collection <T> - ні.
Беван,

@Bevan спробуйте на c #, вони обидва реалізують однаковий набір інтерфейсів
Kylo Ren

1
Це цікавий @KyloRen зміни - вони ж тепер обидва реалізують набір інтерфейсів; цього не було ще в 2008 році.
Беван,

1
@Bevan цікаво. Не впевнений, що було причиною двох різних класів, один з якимись лише додатковими методами.
Кайло Рен

3

На додаток до інших відповідей, я склав короткий огляд загальних переліків та можливостей збору. Колекція обмежена підмножиною Списку:

* = присутній 
o = частково присутній

Збір властивостей / методів < T > Список < T > --------------------------------------- -------      

Add()                *              *
AddRange()                          *
AsReadOnly()                        *
BinarySearch()                      *
Capacity                            *
Clear()              *              *
Contains()           *              *
ConvertAll()                        *
CopyTo()             o              *
Count                *              *
Equals()             *              *
Exists()                            *
Find()                              *
FindAll()                           *
FindIndex()                         *
FindLast()                          *
FindLastIndex()                     *
ForEach()                           *
GetEnumerator()      *              *
GetHashCode()        *              *
GetRange()                          *
GetType()            *              *
IndexOf()            o              *
Insert()             *              *
InsertRange()                       *
Item()               *              *
LastIndexOf()                       *
New()                o              *
ReferenceEquals()    *              *
Remove()             *              *
RemoveAll()                         *
RemoveAt()           *              *
RemoveRange()                       *
Reverse()                           *
Sort()                              *
ToArray()                           *
ToString()           *              *
TrimExcess()                        *
TrueForAll()                        *
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.