142 votes

Comment initialiser une List<T> à une taille donnée (par opposition à la capacité) ?

.NET propose un conteneur de liste générique dont les performances sont presque identiques (voir la question sur les performances des tableaux et des listes). Cependant, ils sont très différents au niveau de l'initialisation.

Les tableaux sont très faciles à initialiser avec une valeur par défaut, et par définition ils ont déjà une certaine taille :

string[] Ar = new string[10];

ce qui permet d'attribuer en toute sécurité des éléments aléatoires, par exemple :

Ar[5]="hello";

avec une liste, les choses sont plus délicates. Je peux voir deux façons de faire la même initialisation, aucune n'est ce qu'on pourrait appeler élégante :

List<string> L = new List<string>(10);
for (int i=0;i<10;i++) L.Add(null);

ou

string[] Ar = new string[10];
List<string> L = new List<string>(Ar);

Quel serait le moyen le plus propre ?

EDIT : Les réponses données jusqu'à présent font référence à la capacité, ce qui est autre chose que de pré-remplir une liste. Par exemple, sur une liste qui vient d'être créée avec une capacité de 10, on ne peut pas faire L[2]="une valeur".

EDIT 2 : Les gens se demandent pourquoi je veux utiliser les listes de cette façon, car ce n'est pas la façon dont elles sont censées être utilisées. Je vois deux raisons :

  1. On pourrait argumenter de manière tout à fait convaincante que les listes sont les tableaux de "nouvelle génération", ajoutant de la flexibilité sans presque aucune pénalité. On devrait donc les utiliser par défaut. Je signale qu'elles ne sont peut-être pas aussi faciles à initialiser.

  2. Ce que j'écris actuellement est une classe de base offrant une fonctionnalité par défaut dans le cadre d'une structure plus large. Dans la fonctionnalité par défaut que je propose, la taille de la liste est connue à l'avance et j'aurais donc pu utiliser un tableau. Cependant, je veux offrir à n'importe quelle classe de base la possibilité de l'étendre dynamiquement et j'opte donc pour une liste.

1 votes

"EDIT : Les réponses données jusqu'à présent font référence à la capacité, ce qui est autre chose que de pré-remplir une liste. Par exemple, sur une liste qui vient d'être créée avec une capacité de 10, on ne peut pas faire L[2]="somevalue"". Compte tenu de cette modification, vous devriez peut-être reformuler le titre de la question...

0 votes

Mais, quelle est l'utilité de pré-remplir une liste avec des valeurs vides, car c'est ce que le topicstarter essaie de faire ?

0 votes

Frederik : Exactement. Quand cela serait-il nécessaire... jamais ?

165voto

List<string> L = new List<string> ( new string[10] );

3 votes

Personnellement, je pense que c'est la manière la plus propre - bien qu'elle ait été mentionnée dans le corps de la question comme pouvant être maladroite - je ne sais pas pourquoi.

4 votes

+1 : Je viens de me trouver dans une situation où j'avais besoin d'une liste de taille variable à initialiser avec un ensemble fixe de nuls avant de la remplir via un index (et d'ajouter des éléments supplémentaires par la suite, donc un tableau ne convenait pas). Cette réponse obtient mon vote pour être pratique et simple.

0 votes

Fabuleuse réponse. @GoneCoding Je me demandais juste si en interne la liste nouvellement initialisée L réutilisera simplement la mémoire de string[10] tel quel (la mémoire de sauvegarde d'une liste est également un tableau) ou il allouera une nouvelle mémoire qui lui est propre et copiera ensuite le contenu de string[10] ? string[10] sera collecté automatiquement si L sélectionne la route la plus tardive.

96voto

Jon Skeet Points 692016

Je ne peux pas dire que j'ai besoin de cela très souvent - pourriez-vous donner plus de détails sur la raison pour laquelle vous voulez cela ? Je le placerais probablement comme une méthode statique dans une classe d'aide :

public static class Lists
{
    public static List<T> RepeatedDefault<T>(int count)
    {
        return Repeated(default(T), count);
    }

    public static List<T> Repeated<T>(T value, int count)
    {
        List<T> ret = new List<T>(count);
        ret.AddRange(Enumerable.Repeat(value, count));
        return ret;
    }
}

Vous pourrait utiliser Enumerable.Repeat(default(T), count).ToList() mais cela serait inefficace en raison du redimensionnement de la mémoire tampon.

EDIT : Comme indiqué dans les commentaires, vous pourriez faire Repeated utiliser une boucle pour remplir la liste si vous le souhaitez. Cela serait aussi un peu plus rapide. Personnellement, je trouve que le code utilisant Repeat plus descriptive, et je soupçonne que dans le monde réel la différence de performance ne serait pas pertinente, mais votre kilométrage peut varier.

1 votes

Je réalise que c'est un vieux post, mais je suis curieux. Enumerable.Repeat s'en sort beaucoup moins bien qu'une boucle for, comme le montre la dernière partie du lien ( dotnetperls.com/initialize-array ). De plus, AddRange() a une complexité O(n) selon msdn. N'est-ce pas un peu contre-productif d'utiliser la solution donnée au lieu d'une simple boucle ?

1 votes

@Jimmy : Les deux approches seront O(n), et je trouve que cette approche est plus descriptive de ce que j'essaie d'obtenir. Si vous préférez une boucle, n'hésitez pas à l'utiliser.

1 votes

@Jimmy : Notez également que le benchmark utilisé ici utilise Enumerable.Repeat(...).ToArray() qui est pas comment je l'utilise.

23voto

Ed S. Points 70246

Utilisez le constructeur qui prend un int ("capacité") comme argument :

List<string> = new List<string>(10);

EDIT : Je devrais ajouter que je suis d'accord avec Frederik. Vous utilisez la liste d'une manière qui va à l'encontre de la raison pour laquelle vous l'avez utilisée en premier lieu.

EDIT2 :

EDIT 2 : Ce que j'écris actuellement est une classe de base offrant une fonctionnalité par défaut dans le cadre d'une structure plus importante. Dans la fonctionnalité par défaut que je propose, la taille de la liste est connue à l'avance et j'aurais donc pu utiliser un tableau. Cependant, je veux offrir à toute classe de base la possibilité de l'étendre dynamiquement et j'opte donc pour une liste.

Pourquoi quelqu'un aurait-il besoin de connaître la taille d'une liste contenant toutes les valeurs nulles ? S'il n'y a aucune valeur réelle dans la liste, je m'attendrais à ce que la longueur soit de 0. Quoi qu'il en soit, le fait que cela soit maladroit démontre que cela va à l'encontre de l'utilisation prévue de la classe.

53 votes

Cette réponse n'alloue pas 10 entrées nulles dans la liste (ce qui était l'exigence), elle alloue simplement de l'espace pour 10 entrées. avant qu'un redimensionnement de la liste ne soit nécessaire (c.-à-d. la capacité), ce qui ne change rien à new List<string>() en ce qui concerne le problème. Bravo pour avoir obtenu autant de votes positifs :)

2 votes

Ce constructeur surchargé est la valeur de la "capacité initiale" et non de la "taille" ou de la "longueur", et il n'initialise pas non plus les éléments.

1 votes

Pour répondre à la question "pourquoi quelqu'un aurait-il besoin de ça" : J'en ai besoin en ce moment pour le clonage profond de structures de données arborescentes. Un nœud peut ou non peupler ses enfants, mais ma classe de nœud de base doit être capable de se cloner avec tous ses nœuds enfants. Bla, bla, c'est encore plus compliqué. Mais j'ai besoin des deux pour remplir ma liste encore vide via list[index] = obj; et d'utiliser d'autres capacités de liste.

11voto

mini998 Points 433

Créez d'abord un tableau avec le nombre d'éléments que vous voulez, puis convertissez le tableau en une liste.

int[] fakeArray = new int[10];

List<int> list = fakeArray.ToList();

8voto

David B Points 53123

Si vous voulez initialiser la liste avec N éléments d'une certaine valeur fixe :

public List<T> InitList<T>(int count, T initValue)
{
  return Enumerable.Repeat(initValue, count).ToList();
}

1 votes

Voir la réponse de John Skeet qui s'inquiète du redimensionnement de la mémoire tampon par cette technique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X