В чем разница между List (of T) и Collection (of T)?

94

Я видел, как они использовались одинаково, и я беспокоюсь, что собираюсь пойти по пути в дизайне, который необратим, если я не пойму это лучше. Кроме того, я использую .NET.

Энтони Поттс
источник

Ответы:

50

Collection<T>- это настраиваемая оболочка IList<T>. Хотя IList<T>он не запечатан, он не предоставляет никаких точек настройки. Collection<T>методы по умолчанию делегированы стандартным IList<T>методам, но их можно легко переопределить для выполнения того, что вы хотите. Также возможно подключать события внутри a, Collection<T>которые, я не верю, можно сделать с помощью IList.

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

Адам Лассек
источник
@Marc Gravell, @Adam Lassek: Разве мы не можем сделать то же самое со списком, скрывая метод InsertItem (...) с общедоступным void new InsertItem (...), а затем вызывая base.InsertItem (.. .) изнутри ? Это все равно не нарушит контракт. (в списке имя «Вставить», но тем не менее). Так что же такого особенного в том, чтобы придерживаться Collections <T>?
DeeStackOverflow
4
@DeeStackOverflow - потому что метод скрытие не будет использоваться любым кодом , который использует различные API - то есть все , что выглядит для IList, IList<T>, и List<T>т.д. Короче говоря, вы не имеете ни малейшего представления , будет ли это назвать. Полиморфизм исправляет это.
Марк Грейвелл
@AdamLassek: Возможно, вы захотите добавить в свой ответ ObservableCollection<T>пример, в котором методы переопределяются для уведомления об изменениях.
Киран Чалла
90

В C # существует три концепции представления набора объектов. В порядке увеличения характеристик это:

  • Enumerable - неупорядоченный, неизменяемый
  • Коллекция - можно добавлять / удалять элементы
  • Список - позволяет элементам иметь порядок (доступ и удаление по индексу)

Enumerable не имеет порядка. Вы не можете добавлять или удалять предметы из набора. Вы даже не можете подсчитать количество предметов в наборе. Это строго позволяет вам получить доступ к каждому элементу в наборе, один за другим.

Коллекция - это изменяемый набор. Вы можете добавлять и удалять объекты из набора, вы также можете получить количество предметов в наборе. Но по-прежнему нет порядка, и потому что нет порядка: нет возможности получить доступ к элементу по индексу, и нет никакого способа сортировки.

Список - это упорядоченный набор объектов. Вы можете отсортировать список, получить доступ к элементам по индексу, удалить элементы по индексу.

Фактически, если посмотреть на их интерфейсы, они основаны друг на друге:

  • interface IEnumerable<T>

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

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

    • Insert
    • IndexOf
    • RemoveAt

При объявлении переменных или параметров метода вы должны использовать

  • IEnumerable
  • ICollection
  • 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>это сумка, в которой вам не разрешен доступ к элементам с помощью index ( ICollection); но вы можете произвольно добавлять и удалять элементы из коллекции
  • SynchronizedCollection<T> в упорядоченной коллекции, где вы можете добавлять / удалять элементы по индексу

Таким образом, вы можете легко изменить:

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

SaveEveryUser(users);

tl; dr

  • Enumerable - доступ к элементам, неупорядоченный, неизменяемый
  • Коллекция - можно изменять (добавлять, удалять, подсчитывать)
  • Список - можно получить доступ по индексу

Выберите нужную вам концепцию , затем используйте соответствующий класс.

Ян Бойд
источник
11
OP спросил о конкретных типах, и вы сравнили интерфейсы. Конкретный тип Collection <T> реализует IList <T> и имеет возможности доступа по индексу.
JJS
2
Ответ хорош, но отклонился от вопроса. Не подходит для вашего ответа для классов Collection <T> и List <T>, я имею в виду с точки зрения вопроса, если я попытаюсь подтвердить вашу точку зрения, они просто не оправдывают. Для общего ответа вы можете быть правы, что коллекция не упорядочена, поэтому нет индексации, но список упорядочен, поэтому вставка возможна по определенному индексу.
Кайло Рен,
что, если бы у меня были функции из ICollection <T>, а также поиск и возможность поиска?
2
Если список упорядочен, а коллекция неупорядочена, это будет огромная функциональная разница, позволяющая легко направлять нового ученика (например, меня) к выбору. Но подождите, почему вы говорите, что в коллекции нет порядка? Он предоставляет методы IndexOf () и RemoveAt () , так что он упорядочен, не так ли? Я что-то здесь пропустил?
RayLuo
1
@RayLuo Я имею в виду именно ICollection<T>и IList<T>. Различные конкретные реализации могут вести себя по-разному. Например, если вы получаете доступ к a List<T>через его IEnumerable<T>интерфейс, то у вас нет возможности добавлять, удалять, сортировать или подсчитывать элементы в списке.
Ian Boyd
44

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

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

Рассмотрите возможность использования Collection<T>в своем коде, если нет конкретных функций, List<T>которые вам нужны.

Выше приведены лишь рекомендации.

[Адаптировано из: Руководство по разработке каркаса, второе издание]

Арнольд Зокас
источник
Стоит отметить, что типы, которые используют любые изменяемые объекты для инкапсуляции своего собственного состояния, должны избегать возврата объектов такого типа, если только у рассматриваемых объектов нет средств уведомления своего владельца, когда они мутируются, или имени метода, возвращающего объект явно подразумевает, что он возвращает новый экземпляр. Обратите внимание, что, например, Dictionary<string, List<string>>для возврата List<string>- это нормально, поскольку состояние словаря инкапсулирует только идентификаторы списков в нем, а не их содержимое.
supercat
37

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

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

С LINQ дополнительные методы List<T>становятся менее важными, поскольку LINQ-to-Objects имеет тенденцию предоставлять их в любом случае ... например First(pred), OrderBy(...)и т. Д.

Марк Гравелл
источник
6
В Collection <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<>почти в два раза быстрее.

редактировать

Я не могу понять, почему люди голосуют против этого. И на моей рабочей машине, и на моей домашней машине код List <> на 80% быстрее.

Tuinstoel
источник
1
Как это быстрее? Погляди? Прошивка? Удаление? Поиск? Почему быстрее?
Doug T.
17
Список
набирает
1
В коллекции меньше методов. Поэтому быстрее. QED. (шучу, я не троллинг)
Ray
2
Пробовал на своей машине, и список стал примерно на 20% быстрее. Было бы интересно обсудить, почему это может быть. Может список лучше с распределением памяти.
Ray
10
Методы списка не наследуются, поэтому нет проверок, были ли они унаследованы; Методы коллекции наследуются. Преимущество состоит в том, что вы можете использовать Collection в качестве базового класса для наследования и создания настраиваемой коллекции.
Ричард Гадсден,
11

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

Ману
источник
4

Это один из тех вопросов аспирантуры. Коллекция T - своего рода абстрактная; может быть реализация по умолчанию (я не парень .net / c #), но коллекция будет иметь базовые операции, такие как добавление, удаление, итерация и т. д.

Список T подразумевает некоторые особенности этих операций: добавление должно занимать постоянное время, удаление должно занимать время, пропорциональное количеству элементов, getfirst должно быть постоянным временем. В общем, список является разновидностью коллекции, но коллекция не обязательно является разновидностью списка.

Чарли Мартин
источник
4

Хансельман говорит : « Collection<T>выглядит как список, и у него даже есть List<T>внутренний. КАЖДЫЙ единственный метод делегирует внутреннему List<T>. Он включает защищенное свойство, которое предоставляет List<T>

РЕДАКТИРОВАТЬ: Collection<T>не существует в System.Generic.Collections .NET 3.5. Если вы переходите с .NET 2.0 на 3.5, вам нужно будет изменить код, если вы используете много Collection<T>объектов, если я не упускаю что-то очевидное ...

РЕДАКТИРОВАТЬ 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/…
амнезия
4

Согласно MSDN, List (Of T) .Add - это «операция O (n)» (когда «Capacity» превышена), а Collection (Of T) .Add - всегда «операция O (1)». Это было бы понятно, если бы List был реализован с использованием массива и коллекции связанного списка. Однако, если бы это было так, можно было бы ожидать, что Collection (Of T) .Item будет «операцией O (n)». Но - это - нет !?! Collection (Of T) .Item - это «операция O (1)», как и List (Of T) .Item.

Вдобавок к этому, сообщение "tuinstoel" "29 декабря '08 в 22:31" выше утверждает, что тесты скорости показывают List (Of T) .Add, чтобы быть быстрее, чем Collection (Of T) .Add, который я воспроизвел с помощью Длинные и струнные. Хотя я получил только ~ 33% быстрее по сравнению с его заявленными 80%, согласно MSDN, должно было быть наоборот и в "n" раз!?!

Том
источник
3

Оба реализуют одни и те же интерфейсы, поэтому они будут вести себя одинаково. Возможно, они по-другому реализованы внутри, но это нужно будет проверить.

Единственные реальные различия, которые я вижу, - это пространства имен и тот факт, который Collection<T>отмечен значком ComVisibleAttribute(false), поэтому код COM не может его использовать.

OwenP
источник
Они реализуют разные интерфейсы - List <T> реализует IList <T>, а Collection <T> - нет.
Беван,
@Bevan, попробуйте его на C #, они оба реализуют один и тот же набор интерфейсов
Кайло Рен,
1
Это интересный @KyloRen изменения - они же теперь оба реализуют набор интерфейсов; в 2008 году этого не было.
Bevan
1
@Bevan интересно. Не уверен, в чем причина двух разных классов, один с некоторыми дополнительными методами.
Kylo Ren
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()                        *
Miroxlav
источник