Jaka jest różnica między List (of T) a Collection (of T)?

94

Widziałem je używane w ten sam sposób i martwię się, że pójdę ścieżką w projektowaniu, która jest nieodwracalna, jeśli nie zrozumiem tego lepiej. Używam również platformy .NET.

Anthony Potts
źródło

Odpowiedzi:

50

Collection<T>to konfigurowalne opakowanie wokół IList<T>. Chociaż IList<T>nie jest zapieczętowany, nie zapewnia żadnych punktów dostosowywania. Collection<T>Metody są domyślnie delegowane do IList<T>metod standardowych , ale można je łatwo zastąpić, aby zrobić to, co chcesz. Możliwe jest również powiązanie zdarzeń wewnątrz elementu Collection<T>, którego nie sądzę, aby można było zrobić za pomocą IList.

Krótko mówiąc, znacznie łatwiej jest go rozszerzyć po fakcie, co może potencjalnie oznaczać znacznie mniej refaktoryzacji.

Adam Lassek
źródło
@Marc Gravell, @Adam Lassek: Czy nie możemy zrobić tego samego z Listą, ukrywając metodę InsertItem (...) z publicznym void new InsertItem (...), a następnie wywołując metodę base.InsertItem (.. .) z wewnątrz ? To nadal nie zerwie kontraktu. (nazwa to „Wstaw” na liście, ale mimo wszystko). Więc o co chodzi w trzymaniu się kolekcji <T>?
DeeStackOverflow
4
@DeeStackOverflow - ponieważ metoda ukrywania nie będzie używany przez jakiegokolwiek kodu, który używa innego API - czyli coś, co szuka IList, IList<T>, List<T>itd. Krótko mówiąc, nie masz pojęcia, czy zostanie ona wywołana. Polimorfizm rozwiązuje ten problem.
Marc Gravell
@AdamLassek: Możesz dodać w swojej odpowiedzi ObservableCollection<T>jako przykład zastąpienie metod powiadamiania o zmianach.
Kiran Challa
88

W języku C # istnieją trzy koncepcje reprezentowania zbioru obiektów. W kolejności rosnących funkcji są to:

  • Enumerable - nieuporządkowany, niemodyfikowalny
  • Kolekcja - może dodawać / usuwać elementy
  • Lista - umożliwia pozycjonowanie pozycji (dostęp i usuwanie według indeksu)

Enumerable nie ma porządku. Nie możesz dodawać ani usuwać elementów z zestawu. Nie możesz nawet policzyć elementów w zestawie. Ściśle umożliwia dostęp do każdego elementu zestawu, jeden po drugim.

Kolekcja to zestaw modyfikowalny. Możesz dodawać i usuwać obiekty z zestawu, możesz też sprawdzić liczbę elementów w zestawie. Ale nadal nie ma porządku, a ponieważ nie ma porządku: nie ma możliwości uzyskania dostępu do elementu według indeksu ani sposobu sortowania.

Lista to uporządkowany zbiór obiektów. Możesz sortować listę, uzyskiwać dostęp do elementów według indeksu, usuwać elementy według indeksu.

W rzeczywistości, patrząc na ich interfejsy, opierają się na sobie:

  • interface IEnumerable<T>

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

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

    • Insert
    • IndexOf
    • RemoveAt

Deklarując zmienne lub parametry metod, należy wybrać opcję

  • IEnumerable
  • ICollection
  • IList

w oparciu o koncepcję, musisz zrobić z zestawem obiektów.

Jeśli chcesz tylko zrobić coś dla każdego obiektu na liście, potrzebujesz tylko IEnumerable:

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

Nie obchodzi mnie, czy użytkownicy są przechowywane w List<T>, Collection<T>, Array<T>czy cokolwiek innego. Potrzebujesz tylkoIEnumerable<T> interfejsu.

Jeśli chcesz mieć możliwość dodawania, usuwania lub liczenia elementów w zestawie, użyj kolekcji :

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

Jeśli zależy Ci na kolejności sortowania i chcesz, aby była ona poprawna, użyj Listy :

IList<User> users = FetchUsers(db);

W formie wykresu:

| 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>I Collection<T>na System.Collections.Genericdwie klasy, które implementują te interfejsy; ale to nie jedyne zajęcia:

  • ConcurrentBag<T>to uporządkowany worek przedmiotów ( IEnumerable<T>)
  • LinkedList<T>to worek, w którym nie masz dostępu do elementów przez index ( ICollection); ale możesz dowolnie dodawać i usuwać elementy z kolekcji
  • SynchronizedCollection<T> w uporządkowanej kolekcji, w której możesz dodawać / usuwać pozycje według indeksu

Możesz więc łatwo zmienić:

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

SaveEveryUser(users);

tl; dr

  • Niezliczone - przedmioty dostępu, nieuporządkowane, niemodyfikowalne
  • Kolekcja - można modyfikować (dodawać, usuwać, liczyć)
  • Lista - można uzyskać dostęp według indeksu

Wybierz potrzebną koncepcję , a następnie użyj pasującej klasy.

Ian Boyd
źródło
11
OP zapytał o konkretne typy, a ty porównałeś interfejsy. Typ konkretny Collection <T> implementuje IList <T> i ma dostęp za pomocą funkcji indeksu.
JJS
2
Odpowiedź jest dobra, ale odbiegała od pytania. Nie pasuje do twojej odpowiedzi dla klas Collection <T> i List <T>, mam na myśli pytanie potencjalne, jeśli spróbuję potwierdzić twój punkt widzenia, po prostu nie uzasadniają. Dla ogólnej odpowiedzi możesz mieć rację, że zbiór nie jest uporządkowany, więc nie ma indeksowania, ale lista jest uporządkowana, aby możliwe było wstawienie do określonego indeksu.
Kylo Ren
co jeśli chciałbym mieć funkcje z ICollection <T>, a także rozwiązywać i znaleźć możliwość?
2
Jeśli lista jest uporządkowana, a kolekcja nieuporządkowana, byłaby to ogromna różnica funkcjonalna, która ułatwiłaby nowemu uczniowi (takiemu jak ja) dokonanie wyboru. Ale czekaj, dlaczego mówisz, że kolekcja nie ma porządku? Udostępnia metody IndexOf () i RemoveAt () , więc JEST uporządkowane, prawda? Czy coś mi tu umknęło?
RayLuo
1
@RayLuo Mam na myśli konkretnie ICollection<T>i IList<T>. Różne konkretne implementacje mogą zachowywać się inaczej. Na przykład, jeśli uzyskujesz dostęp do a List<T>przez jej IEnumerable<T>interfejs, nie masz możliwości dodawania, usuwania, sortowania ani liczenia elementów na liście.
Ian Boyd
44

List<T>jest przeznaczony do użytku wewnętrznego w kodzie aplikacji. Należy unikać pisania publicznych interfejsów API, które akceptują lub zwracają List<T>(zamiast tego rozważ użycie nadklasy lub interfejsu kolekcji).

Collection<T> obsługuje klasę bazową dla kolekcji niestandardowych (chociaż można jej używać bezpośrednio).

Rozważ użycie Collection<T>w swoim kodzie, chyba że potrzebujesz określonych funkcji List<T>.

Powyższe to tylko zalecenia.

[Na podstawie: Wytyczne dotyczące projektowania ram, wydanie drugie]

Arnold Zokas
źródło
Warto zauważyć, że typy, które używają dowolnego rodzaju obiektów mutowalnych do hermetyzacji własnego stanu, powinny unikać zwracania obiektów tego typu, chyba że przedmiotowe obiekty mają sposób powiadamiania właściciela, gdy są zmutowane, lub nazwa metody zwracającej wartość obiekt jasno sugeruje, że zwraca nową instancję. Należy pamiętać, że na przykład Dictionary<string, List<string>>do powrotu List<string>jest po prostu w porządku, ponieważ stan słownika za obudowuje jedynie tożsamość wykazów w nim, zamiast ich zawartości.
supercat
37

List<T>jest bardzo często spotykanym kontenerem, ponieważ jest bardzo uniwersalny (z wieloma przydatnymi metodami, takimi jak Sort,Find itp) - ale nie ma punktów rozszerzenia, jeżeli chcesz nadpisać dowolny zachowania (elementy kontrolne na wkładkę, na przykład).

Collection<T>jest opakowaniem wokół dowolnego IList<T>(domyślnie List<T>) - ma punkty rozszerzeń ( virtualmetody), ale nie ma tak wielu metod wsparcia, jak Find. Z powodu pośredniego kierunku jest nieco wolniejszy niż List<T>, ale niewiele.

Z LINQ, dodatkowe metody w List<T>stają się mniej ważne, ponieważ LINQ-Objects dąży do zapewnienia im tak ... na przykład First(pred), OrderBy(...)itd

Marc Gravell
źródło
6
Collection <T> nie ma metody foreach, nawet w Linq-to-Objects.
tuinstoel
7
@tuinstoel - ale dodanie tego jest trywialne.
Marc Gravell
12

Lista jest szybsza.

Zrób na przykład

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());


}

na moim komputerze List<> jest prawie dwa razy szybszy.

Edytować

Nie rozumiem, dlaczego ludzie to odrzucają. Zarówno na moim komputerze roboczym, jak i komputerze domowym kod List <> jest o 80% szybszy.

tuinstoel
źródło
1
Jak to jest szybsze? Wyszukaj? Wprowadzenie? Usuwanie? Szukaj? Dlaczego to jest szybsze?
Doug T.
17
Lista zawiera mniej liter do
wpisania
1
Zbieranie ma mniej metod. Dlatego jest szybszy. CO BYŁO DO OKAZANIA. (żartuję, nie trolluję)
Ray,
2
Wypróbowałem to na moim komputerze i lista jest około 20% szybsza. Byłbym zainteresowany dyskusją na temat tego, dlaczego tak się dzieje. Może lista jest lepsza przy przydzielaniu pamięci.
Ray
10
Metody listy nie są dziedziczone, więc nie ma sprawdzania, czy zostały odziedziczone; Metody kolekcji są dziedziczone. Zaletą jest to, że możesz używać Collection jako klasy bazowej do dziedziczenia i tworzenia niestandardowej kolekcji.
Richard Gadsden,
11

Lista reprezentuje kolekcję, w której kolejność elementów jest ważna. Obsługuje również metody sortowania i wyszukiwania. Zbieranie jest bardziej ogólną strukturą danych, która przyjmuje mniej założeń dotyczących danych, a także obsługuje mniej metod manipulowania nimi. Jeśli chcesz ujawnić niestandardową strukturę danych, prawdopodobnie powinieneś rozszerzyć kolekcję. Jeśli potrzebujesz manipulować danymi bez ujawniania struktury danych, prawdopodobnie wygodniejszym sposobem jest lista.

Manu
źródło
4

To jedno z tych pytań w szkole średniej. Kolekcja T jest czymś w rodzaju abstrakcji; może istnieć domyślna implementacja (nie jestem facetem .net / c #), ale kolekcja będzie miała podstawowe operacje, takie jak dodawanie, usuwanie, iteracja i tak dalej.

Lista T sugeruje pewne szczegóły dotyczące tych operacji: dodawanie powinno zajmować stały czas, usuwanie powinno zajmować czas proporcjonalny do liczby elementów, getfirst powinno być zgodne z czasem. Ogólnie lista jest rodzajem kolekcji, ale kolekcja niekoniecznie jest rodzajem listy.

Charlie Martin
źródło
4

Hanselman Mówi : " Collection<T>wygląda jak lista, a nawet ma List<T>wewnętrznie. KAŻDA pojedyncza metoda deleguje do wewnętrznej List<T>. Zawiera chronioną właściwość, która ujawnia List<T>."

EDYCJA: Collection<T>nie istnieje w System.Generic.Collections .NET 3.5. Jeśli przeprowadzasz migrację z .NET 2.0 do 3.5, musisz zmienić kod, jeśli używasz dużoCollection<T> obiektów, chyba że brakuje mi czegoś oczywistego ...

EDYCJA 2: Collection<T>jest teraz w przestrzeni nazw System.Collections.ObjectModel w .NET 3.5. Plik pomocy mówi tak:

„Przestrzeń nazw System.Collections.ObjectModel zawiera klasy, które mogą być używane jako kolekcje w modelu obiektów biblioteki wielokrotnego użytku. Tych klas należy używać, gdy właściwości lub metody zwracają kolekcje”.

Tad Donaghe
źródło
4

Wszystkie te interfejsy dziedziczą, z IEnumerablektórych należy się upewnić. Ten interfejs w zasadzie umożliwia użycie klasy w instrukcji foreach (w C #).

  • ICollectionto najbardziej podstawowy z wymienionych interfejsów. Jest to wyliczalny interfejs, który obsługuje Counti to wszystko.
  • IListjest wszystkim, co ICollectionjest, ale obsługuje także dodawanie i usuwanie elementów, pobieranie elementów według indeksu, itp. Jest to najczęściej używany interfejs dla „list obiektów”, który jest niejasny, jaki znam.
  • IQueryablejest wyliczalnym interfejsem obsługującym LINQ. Zawsze możesz utworzyć IQueryablez IList i użyć LINQ to Objects, ale możesz również znaleźć IQueryableużywane do odroczonego wykonywania instrukcji SQL w LINQ to SQL i LINQ to Entities.
  • IDictionaryjest innym zwierzęciem w tym sensie, że jest odwzorowaniem unikalnych kluczy na wartości. Jest również wyliczalny, ponieważ możesz wyliczyć pary klucz / wartość, ale poza tym służy innym celom niż inne wymienione przez Ciebie
Raj Gupta
źródło
ICollection obsługuje dodawanie / usuwanie / czyszczenie: msdn.microsoft.com/en-us/library/…
amnesia
4

Według MSDN List (Of T) .Add to „operacja O (n)” (gdy „Capacity” jest przekroczona), podczas gdy Collection (Of T) .Add jest zawsze „operacją O (1)”. Byłoby to zrozumiałe, gdyby lista została zaimplementowana przy użyciu tablicy, a kolekcja - lista połączona. Jednak gdyby tak było, należałoby oczekiwać, że Collection (Of T) .Item będzie „operacją O (n)”. Ale - to - nie !?! Collection (Of T) .Item jest „operacją O (1)”, podobnie jak List (Of T) .Item.

Co więcej, powyższy post „tuinstoel” z 29 grudnia 2008 o 22:31 twierdzi, że testy prędkości pokazują List (Of T) .Dodaj, aby być szybszym niż Collection (Of T). Dodaj, który odtworzyłem z Długie i Stringi. Chociaż osiągnąłem tylko ~ 33% szybciej niż jego deklarowane 80%, według MSDN powinno być odwrotnie i o „n” razy!?!

Tomek
źródło
3

Oba implementują te same interfejsy, więc będą się zachowywać w ten sam sposób. Być może są one wdrażane wewnętrznie inaczej, ale należałoby to przetestować.

Jedyne rzeczywiste różnice, które widzę, to przestrzenie nazw i fakt, że Collection<T>jest oznaczony ComVisibleAttribute(false), więc kod COM nie może go używać.

OwenP
źródło
Implementują różne interfejsy - List <T> implementuje IList <T>, gdzie Collection <T> nie.
Bevan
@Bevan wypróbuj to w C #, obaj implementują ten sam zestaw interfejsów
Kylo Ren
1
To interesujący @KyloRen Change - oni mają teraz zarówno realizować ten sam zestaw interfejsów; tak nie było w 2008 roku.
Bevan
1
@Bevan interesujące. Nie jestem pewien, co było przyczyną dwóch różnych klas, jednej z kilkoma dodatkowymi metodami.
Kylo Ren,
3

Oprócz innych odpowiedzi, opracowałem szybki przegląd ogólnych możliwości list i kolekcji. Kolekcja jest ograniczonym podzbiorem Listy:

* = obecny
o = częściowo obecny

Property / Method Collection <T> List <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
źródło