web-dev-qa-db-fra.com

Comment initialiser une liste <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 Performances des tableaux contre des listes). Cependant, ils sont assez différents en 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 la liste les choses sont plus délicates. Je peux voir deux manières de faire la même initialisation, aucune d’elles n’étant ce que vous appelleriez une é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);

Quelle serait une manière plus propre?

EDIT: Jusqu'à présent, les réponses se réfèrent à 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"

EDIT 2: Les gens se demandent pourquoi je veux utiliser les listes de cette façon, car ce n'est pas la manière dont elles sont destinées. Je peux voir deux raisons:

  1. On pourrait affirmer de manière assez convaincante que les listes sont la matrice de la "prochaine génération", ce qui ajoute de la flexibilité sans pénalité. Par conséquent, il convient de les utiliser par défaut. Je souligne qu'ils ne seront peut-être pas aussi faciles à initialiser.

  2. Ce que je suis en train d’écrire est une classe de base offrant des fonctionnalités par défaut dans un cadre plus large. Dans la fonctionnalité par défaut que je propose, la taille de la liste est connue et, par conséquent, j'aurais pu utiliser un tableau. Cependant, je souhaite offrir à toutes les classes de base la possibilité de l’étendre de manière dynamique et j’optent donc pour une liste.

96
Boaz

Je ne peux pas dire que j'en ai besoin très souvent - pourriez-vous donner plus de détails sur les raisons pour lesquelles vous le souhaitez? Je le mettrais probablement comme méthode statique dans une classe d'assistance:

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 pourriez utiliser Enumerable.Repeat(default(T), count).ToList() mais cela serait inefficace en raison du redimensionnement de la mémoire tampon.

Notez que si T est un type de référence, il stockera count des copies de la référence transmise pour le paramètre value - elles feront donc toutes référence au même objet. Cela peut être ou ne pas être ce que vous voulez, selon votre cas d'utilisation.

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

72
Jon Skeet
List<string> L = new List<string> ( new string[10] );
112
NewBe

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

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

EDIT: Je dois ajouter que je suis d'accord avec Frederik. Vous utilisez la liste d'une manière qui va à l'encontre de l'ensemble du raisonnement derrière son utilisation.

EDIT2:

EDIT 2: Ce que je suis en train d’écrire est une classe de base offrant des fonctionnalités par défaut dans le cadre d’un cadre plus vaste. Dans la fonctionnalité par défaut que je propose, la taille de la liste est connue et, par conséquent, j'aurais pu utiliser un tableau. Cependant, je souhaite offrir à toutes les classes de base la possibilité de l’étendre de manière dynamique et j’optent donc pour une liste.

Pourquoi quelqu'un aurait-il besoin de connaître la taille d'une liste avec toutes les valeurs NULL? S'il n'y a pas de vraies valeurs dans la liste, je m'attendrais à ce que la longueur soit égale à 0. Quoi qu'il en soit, le fait qu'il s'agisse d'une imprécision démontre que cela va à l'encontre de l'utilisation prévue de la classe.

24
Ed S.

Pourquoi utilisez-vous une liste si vous souhaitez l'initialiser avec une valeur fixe? Je peux comprendre que, pour des raisons de performance, vous souhaitiez lui attribuer une capacité initiale, mais l’un des avantages d’une liste par rapport à un tableau classique est-il que celui-ci peut s’accroître en cas de besoin?

Quand tu fais ça:

List<int> = new List<int>(100);

Vous créez une liste dont la capacité est de 100 entiers. Cela signifie que votre liste n'aura pas besoin de "grandir" jusqu'à ce que vous ajoutiez le 101ème élément. Le tableau sous-jacent de la liste sera initialisé avec une longueur de 100.

7
Frederik Gheysels

Initialiser le contenu d'une liste comme celle-là n'est pas vraiment ce à quoi servent les listes. Les listes sont conçues pour contenir des objets. Si vous souhaitez mapper des nombres particuliers sur des objets particuliers, envisagez d'utiliser une structure de paire clé-valeur, telle qu'une table de hachage ou un dictionnaire, au lieu d'une liste.

5
Welbog

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

int[] fakeArray = new int[10];

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

Vous semblez insister sur la nécessité d'une association de position avec vos données, un tableau associatif ne serait-il pas plus approprié?

Dictionary<int, string> foo = new Dictionary<int, string>();
foo[2] = "string";
4
Greg D

Si vous souhaitez initialiser la liste avec N éléments d’une valeur fixe:

public List<T> InitList<T>(int count, T initValue)
{
  return Enumerable.Repeat(initValue, count).ToList();
}
4
Amy B
string [] temp = new string[] {"1","2","3"};
List<string> temp2 = temp.ToList();
1
Henk

La réponse acceptée (celle avec la coche verte) a un problème.

Le problème:

var result = Lists.Repeated(new MyType(), sizeOfList);
// each item in the list references the same MyType() object
// if you edit item 1 in the list, you are also editing item 2 in the list

Je recommande de changer la ligne ci-dessus pour effectuer une copie de l'objet. Il y a beaucoup d'articles différents à ce sujet:

Si vous souhaitez initialiser chaque élément de votre liste avec le constructeur par défaut plutôt que NULL, ajoutez la méthode suivante:

public static List<T> RepeatedDefaultInstance<T>(int count)
    {
        List<T> ret = new List<T>(count);
        for (var i = 0; i < count; i++)
        {
            ret.Add((T)Activator.CreateInstance(typeof(T)));
        }
        return ret;
    }
1
Jeremy Ray Brown

Un avis concernant IList: MSDN IList Remarks : "Les implémentations IList se divisent en trois catégories: lecture seule, taille fixe et taille variable. (...). Pour la version générique de cette interface , voir System.Collections.Generic.IList<T>. "

IList<T> _ n'hérite PAS de IList (mais List<T> implémente les deux IList<T> et IList), mais est toujours taille variable . Depuis .NET 4.5, nous avons aussi IReadOnlyList<T> mais, autant que je sache, il n’existe pas de liste générique de taille fixe qui correspond à ce que vous recherchez.

0
EricBDev

Vous pouvez utiliser Linq pour initialiser intelligemment votre liste avec une valeur par défaut. (Similaire à réponse de David B .)

var defaultStrings = (new int[10]).Select(x => "my value").ToList();

Allez un pas plus loin et initialisez chaque chaîne avec des valeurs distinctes "chaîne 1", "chaîne 2", "chaîne 3", etc.:

int x = 1;
var numberedStrings = (new int[10]).Select(x => "string " + x++).ToList();
0
James Lawruk

Ceci est un exemple que j'ai utilisé pour mon test unitaire. J'ai créé une liste d'objet de classe. Ensuite, j'ai utilisé forloop pour ajouter 'X' nombre d'objets que j'attends du service. De cette façon, vous pouvez ajouter/initialiser une liste pour n’importe quelle taille.

public void TestMethod1()
    {
        var expected = new List<DotaViewer.Interface.DotaHero>();
        for (int i = 0; i < 22; i++)//You add empty initialization here
        {
            var temp = new DotaViewer.Interface.DotaHero();
            expected.Add(temp);
        }
        var nw = new DotaHeroCsvService();
        var items = nw.GetHero();

        CollectionAssert.AreEqual(expected,items);


    }

J'espère que je vous ai aidé.

0
Abhay Shiro

Un peu tard, mais la première solution que vous proposez me semble bien plus propre: vous n'allouez pas la mémoire deux fois. Même List constrcutor a besoin de parcourir un tableau pour le copier; il ne sait même pas d'avance qu'il n'y a que des éléments nuls à l'intérieur.

1. - allouer N - boucle N Coût: 1 * allouer (N) + N * loop_iteration

2. - allouer N - allouer N + boucle () Coût: 2 * allouer (N) + N * loop_iteration

Cependant, l'allocation et les boucles de List peuvent être plus rapides car List est une classe intégrée, mais C # est trop compilé par jit ...

0
Victor Drouin