Quelle est la différence entre List (of T) et Collection (of T)?

93

Je les ai vus utilisés de la même manière et je crains de m'engager dans une voie de conception irréversible si je ne comprends pas mieux cela. En outre, j'utilise .NET.

Anthony Potts
la source

Réponses:

50

Collection<T>est un wrapper personnalisable IList<T>. Bien qu'il IList<T>ne soit pas scellé, il ne fournit aucun point de personnalisation. Collection<T>Les méthodes de sont par défaut déléguées aux IList<T>méthodes standard , mais peuvent être facilement remplacées pour faire ce que vous voulez. Il est également possible de connecter des événements à l'intérieur d'un Collection<T>qui, je ne pense pas, pourrait être fait avec un IList.

En bref, il est beaucoup plus facile de l'étendre après coup, ce qui pourrait potentiellement signifier beaucoup moins de refactoring.

Adam Lassek
la source
@Marc Gravell, @Adam Lassek: Ne pouvons-nous pas faire la même chose avec une List en masquant la méthode InsertItem (...) avec un nouvel InsertItem public void (...) puis en appelant la base.InsertItem (.. .) de l'Intérieur ? Cela ne romprait toujours pas le contrat. (le nom est «Insérer» dans la liste, mais néanmoins). Alors, quel est le problème de s'en tenir à Collections <T>?
DeeStackOverflow
4
@DeeStackOverflow - parce que cacher la méthode ne sera pas utilisé par un code qui utilise une API différente - à savoir tout ce qui ressemble pour IList, IList<T>, List<T>etc. Bref, vous ne savez pas si elle sera appelée. Le polymorphisme résout ce problème.
Marc Gravell
@AdamLassek: Vous voudrez peut-être ajouter dans votre réponse à ObservableCollection<T>titre d'exemple où les méthodes sont remplacées pour notifier les modifications.
Kiran Challa
84

En C #, il existe trois concepts pour représenter un sac d'objets. Par ordre croissant de fonctionnalités, ce sont:

  • Énumérable - non ordonné, non modifiable
  • Collection - peut ajouter / supprimer des éléments
  • Liste - permet aux articles d'avoir un ordre (accès et suppression par index)

Enumerable n'a pas d'ordre. Vous ne pouvez pas ajouter ou supprimer des éléments de l'ensemble. Vous ne pouvez même pas obtenir le nombre d'articles dans l'ensemble. Il vous permet strictement d'accéder à chaque élément de l'ensemble, l'un après l'autre.

La collection est un ensemble modifiable. Vous pouvez ajouter et supprimer des objets de l'ensemble, vous pouvez également obtenir le nombre d'éléments dans l'ensemble. Mais il n'y a toujours pas d'ordre, et parce qu'il n'y a pas d'ordre: aucun moyen d'accéder à un élément par index, ni aucun moyen de trier.

La liste est un ensemble ordonné d'objets. Vous pouvez trier la liste, accéder aux éléments par index, supprimer des éléments par index.

En fait, quand on regarde les interfaces pour ceux-ci, ils s'appuient les uns sur les autres:

  • interface IEnumerable<T>

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

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

    • Insert
    • IndexOf
    • RemoveAt

Lors de la déclaration de variables ou de paramètres de méthode, vous devez choisir d'utiliser

  • IEnumerable
  • ICollection
  • IList

basé sur conceptuellement, vous devez faire avec l'ensemble des objets.

Si vous avez juste besoin de pouvoir faire quelque chose sur chaque objet d'une liste, il vous suffit de IEnumerable:

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

Vous ne vous inquiétez pas si les utilisateurs sont conservés dans un List<T>, Collection<T>, Array<T>ou toute autre chose. Vous n'avez besoin que de l' IEnumerable<T>interface.

Si vous avez besoin de pouvoir ajouter, supprimer ou compter les éléments d'un ensemble, utilisez une collection :

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

Si vous vous souciez d'un ordre de tri et que vous avez besoin que l'ordre soit correct, utilisez une liste :

IList<User> users = FetchUsers(db);

Sous forme de graphique:

| 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        |

Les List<T>et Collection<T>in System.Collections.Genericsont deux classes qui implémentent ces interfaces; mais ce ne sont pas les seules classes:

  • ConcurrentBag<T>est un sac d'objets ordonné ( IEnumerable<T>)
  • LinkedList<T>est un sac dans lequel vous n'êtes pas autorisé à accéder aux éléments par index ( ICollection); mais vous pouvez ajouter et supprimer arbitrairement des éléments de la collection
  • SynchronizedCollection<T> dans une collection ordonnée, où vous pouvez ajouter / supprimer des éléments par index

Ainsi, vous pouvez facilement changer:

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

SaveEveryUser(users);

tl; dr

  • Énumérable - éléments d'accès, non ordonnés, non modifiables
  • Collection - peut être modifiée (ajouter, supprimer, compter)
  • Liste - peut accéder par index

Choisissez le concept dont vous avez besoin, puis utilisez la classe correspondante.

Ian Boyd
la source
11
L'OP a posé des questions sur les types concrets et vous avez comparé les interfaces. Le type concret Collection <T> implémente IList <T> et dispose de capacités d'accès par index.
JJS
2
La réponse est bonne mais s'écarte de la question. Ne correspond pas à votre réponse pour les classes Collection <T> et List <T>, je veux dire à partir d'une question prospective si j'essaie de valider votre point, ils ne justifient tout simplement pas. Pour une réponse générale, vous avez peut-être raison de dire que la collection n'est pas ordonnée, donc pas d'indexation, mais que la liste est ordonnée de sorte que l'insertion est possible à un certain index.
Kylo Ren
Et si je voulais avoir des fonctionnalités d'ICollection <T> et aussi résoudre et trouver des possibilités?
2
Si la liste est ordonnée et que la collection est sans ordre, ce serait une énorme différence fonctionnelle pour guider facilement un nouvel apprenant (comme moi) dans son choix. Mais attendez, pourquoi dites-vous qu'une collection n'a pas de commande? Il fournit les méthodes IndexOf () et RemoveAt () , donc il est ordonné, n'est-ce pas? Ai-je manqué quelque chose ici?
RayLuo
1
@RayLuo Je fais référence spécifiquement à ICollection<T>et IList<T>. Différentes implémentations concrètes peuvent se comporter différemment. Par exemple, si vous accédez à un List<T>via son IEnumerable<T>interface, vous n'avez aucun moyen d'ajouter, de supprimer, de trier ou de compter des éléments dans la liste.
Ian Boyd
43

List<T>est destiné à un usage interne dans le code de l'application. Vous devez éviter d'écrire des API publiques qui acceptent ou renvoient List<T>(envisagez d'utiliser une superclasse ou une interface de collection à la place).

Collection<T> sert une classe de base pour les collections personnalisées (bien qu'elle puisse être utilisée directement).

Pensez à utiliser Collection<T>dans votre code, sauf si List<T>vous avez besoin de fonctionnalités spécifiques .

Ce ne sont que des recommandations.

[Adapté de: Framework Design Guidelines, deuxième édition]

Arnold Zokas
la source
Il convient de noter que les types qui utilisent n'importe quel type d'objets mutables pour encapsuler leur propre état doivent éviter de renvoyer des objets de ce type à moins que les objets en question n'aient un moyen de notifier leur propriétaire lorsqu'ils sont mutés, ou le nom de la méthode renvoyant le object implique clairement qu'il renvoie une nouvelle instance. Notez que pour par exemple a Dictionary<string, List<string>>retourner List<string>est très bien, puisque l'état du dictionnaire encapsule uniquement les identités des listes qu'il contient, plutôt que leur contenu.
supercat
37

List<T>est un récipient très souvent vu, car il est tellement polyvalent (avec beaucoup de méthodes pratiques comme Sort, Find, etc.) - mais n'a pas des points d'extension si vous voulez remplacer l' un des comportements (éléments de contrôle à l' insertion, par exemple).

Collection<T>est un wrapper autour de tout IList<T>(par défaut List<T>) - il a les points d'extension ( virtualméthodes), mais pas autant de méthodes de support que Find. En raison de l'indirection, il est légèrement plus lent que List<T>, mais pas de beaucoup.

Avec LINQ, les méthodes supplémentaires en List<T>deviennent moins importantes, puisque LINQ-à-objets tend à leur fournir de toute façon ... par exemple First(pred), OrderBy(...)etc.

Marc Gravell
la source
6
La collection <T> manque de méthode foreach, même dans Linq-to-Objects.
tuinstoel
7
@tuinstoel - mais c'est simple à ajouter.
Marc Gravell
12

La liste est plus rapide.

Faites par exemple

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


}

sur ma machine List<>est presque deux fois plus rapide.

Éditer

Je ne peux pas comprendre pourquoi les gens votent contre cela. Tant sur ma machine de travail que sur ma machine domestique, le code List <> est 80% plus rapide.

tuinstoel
la source
1
Comment est-ce plus rapide? Chercher? Insertion? Suppression? Chercher? Pourquoi est-ce plus rapide?
Doug
17
La liste
contient
1
La collecte a moins de méthodes. C'est donc plus rapide. QED. (blague, je ne suis pas à la traîne)
Ray
2
Je l'ai essayé sur ma machine et la liste est environ 20% plus rapide. Serait intéressé par une discussion sur pourquoi cela pourrait être. Peut-être que la liste est meilleure avec l'allocation de mémoire.
Ray
10
Les méthodes de List ne sont pas héritables, il n'y a donc pas de vérifications pour voir si elles ont été héritées; Les méthodes de la collection sont héritables. L'avantage est que vous pouvez utiliser Collection comme classe de base pour hériter et créer une collection personnalisée.
Richard Gadsden
11

La liste représente une collection où l'ordre des éléments est important. Il prend également en charge les méthodes de tri et de recherche. La collecte est une structure de données plus générale qui fait moins d'hypothèses sur les données et prend également en charge moins de méthodes pour les manipuler. Si vous souhaitez exposer une structure de données personnalisée, vous devez probablement étendre la collection. Si vous avez besoin de manipuler des données sans exposer la structure de données, une liste est probablement le moyen le plus pratique.

Manu
la source
4

C'est l'une de ces questions des écoles supérieures. Une collection de T est une sorte d'abstrait; il peut y avoir une implémentation par défaut (je ne suis pas un gars .net / c #) mais une collection aura des opérations de base comme ajouter, supprimer, itérer, etc.

La liste de T implique quelques spécificités sur ces opérations: add devrait prendre un temps constant, remove devrait prendre un temps proportionnel au nombre d'éléments, getfirst devrait être un temps constant. En général, une liste est une sorte de collection, mais une collection n'est pas nécessairement une sorte de liste.

Charlie Martin
la source
4

Hanselman Speaks : " Collection<T>ressemble à une liste, et il en a même une en List<T>interne. CHAQUE méthode est déléguée à l'interne List<T>. Elle inclut une propriété protégée qui expose le List<T>."

EDIT: Collection<T>n'existe pas dans System.Generic.Collections .NET 3.5. Si vous migrez de .NET 2.0 vers 3.5, vous devrez changer du code si vous utilisez beaucoup d' Collection<T>objets, sauf si je manque quelque chose d'évident ...

EDIT 2: Collection<T>est maintenant dans l'espace de noms System.Collections.ObjectModel dans .NET 3.5. Le fichier d'aide dit ceci:

«L'espace de noms System.Collections.ObjectModel contient des classes qui peuvent être utilisées comme collections dans le modèle objet d'une bibliothèque réutilisable. Utilisez ces classes lorsque des propriétés ou des méthodes renvoient des collections.»

Tad Donaghe
la source
4

Toutes ces interfaces héritent de IEnumerablece que vous devez vous assurer de comprendre. Cette interface vous permet essentiellement d'utiliser la classe dans une instruction foreach (en C #).

  • ICollectionest la plus élémentaire des interfaces que vous avez répertoriées. C'est une interface énumérable qui prend en charge un Countet c'est à peu près tout.
  • IListest tout ce qui ICollectionest, mais il prend également en charge l'ajout et la suppression d'éléments, la récupération d'éléments par index, etc. C'est l'interface la plus couramment utilisée pour les "listes d'objets", ce qui est vague je sais.
  • IQueryableest une interface énumérable qui prend en charge LINQ. Vous pouvez toujours créer un IQueryablefichier à partir d'un IList et utiliser LINQ to Objects, mais vous le trouvez également IQueryableutilisé pour l'exécution différée d'instructions SQL dans LINQ to SQL et LINQ to Entities.
  • IDictionaryest un animal différent dans le sens où il s'agit d'une cartographie des clés uniques aux valeurs. Il est également énumérable dans la mesure où vous pouvez énumérer les paires clé / valeur, mais sinon, il sert un objectif différent des autres que vous avez énumérés
Raj Gupta
la source
ICollection supports ajout / suppression / compensation: msdn.microsoft.com/en-us/library/...
amnésie
4

Selon MSDN, List (Of T) .Add est "une opération O (n)" (lorsque "Capacity" est dépassé) tandis que Collection (Of T) .Add est toujours "une opération O (1)". Cela serait compréhensible si List est implémenté à l'aide d'un tableau et Collection une liste liée. Cependant, si tel était le cas, on s'attendrait à ce que Collection (Of T) .Item soit "une opération O (n)". Mais - ce n'est - pas !?! Collection (Of T) .Item est "une opération O (1)" tout comme List (Of T) .Item.

En plus de cela, le message de "tuinstoel" "29 décembre 08 à 22:31" ci-dessus indique que les tests de vitesse montrent List (Of T) .Ajoutez pour être plus rapide que Collection (Of T) .Add que j'ai reproduit avec Long et String. Bien que je ne sois que 33% plus rapide que le sien revendiqué à 80%, selon MSDN, cela aurait dû être le contraire et de "n" fois!?!

À M
la source
3

Les deux implémentent les mêmes interfaces, donc ils se comportent de la même manière. Peut-être qu'ils sont mis en œuvre différemment en interne, mais cela devrait être testé.

Les seules vraies différences que je vois sont les espaces de noms et le fait qui Collection<T>est marqué par ComVisibleAttribute(false), donc le code COM ne peut pas l'utiliser.

OwenP
la source
Ils implémentent différentes interfaces - List <T> implémente IList <T>, contrairement à Collection <T>.
Bevan
@Bevan l'essaie en c #, ils implémentent tous les deux le même ensemble d'interfaces
Kylo Ren
1
C'est un changement intéressant @KyloRen - ils ne mettent en œuvre maintenant à la fois le même ensemble d'interfaces; ce n'était pas le cas en 2008.
Bevan
1
@Bevan intéressant. Je ne sais pas quelle était la raison de deux classes différentes, une avec juste quelques méthodes supplémentaires.
Kylo Ren
3

En plus d'autres réponses, j'ai compilé un aperçu rapide de la liste générique et des capacités de collecte. La collection est un sous-ensemble limité de la liste:

* = présent 
o = partiellement présent

Propriété / Méthode Collection < T > Liste < 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
la source