it-swarm.com.de

Wie initialisiere ich eine Liste <T> auf eine bestimmte Größe (im Gegensatz zur Kapazität)?

.NET bietet einen generischen Listencontainer, dessen Leistung nahezu identisch ist (siehe Frage Leistung von Arrays vs. Listen). Sie unterscheiden sich jedoch in der Initialisierung.

Arrays können sehr einfach mit einem Standardwert initialisiert werden und haben per Definition bereits eine bestimmte Größe:

string[] Ar = new string[10];

Womit man zufällige Gegenstände sicher zuordnen kann, sagen wir:

Ar[5]="hello";

mit Liste sind die Dinge schwieriger. Es gibt zwei Möglichkeiten, die gleiche Initialisierung durchzuführen, von denen Sie keine als elegant bezeichnen würden:

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

oder

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

Was wäre ein sauberer Weg?

BEARBEITEN: Die bisherigen Antworten beziehen sich auf die Kapazität, die etwas anderes ist, als eine Liste vorab auszufüllen. Zum Beispiel kann man auf einer Liste, die gerade mit einer Kapazität von 10 erstellt wurde, nicht L[2]="somevalue"

EDIT 2: Die Leute fragen sich, warum ich Listen auf diese Weise verwenden möchte, da dies nicht die Art ist, wie sie verwendet werden sollen. Ich kann zwei Gründe sehen:

  1. Man könnte durchaus überzeugend argumentieren, dass Listen die "nächste Generation" von Arrays sind, die Flexibilität ohne Beeinträchtigung bieten. Daher sollte man sie standardmäßig verwenden. Ich weise darauf hin, dass sie möglicherweise nicht so einfach zu initialisieren sind.

  2. Was ich gerade schreibe, ist eine Basisklasse, die Standardfunktionen als Teil eines größeren Frameworks anbietet. In der Standardfunktionalität, die ich anbiete, ist die Größe der Liste in erweitert bekannt und daher hätte ich ein Array verwenden können. Ich möchte jedoch jeder Basisklasse die Möglichkeit bieten, sie dynamisch zu erweitern, und entscheide mich daher für eine Liste.

96
Boaz

Ich kann nicht sagen, dass ich das sehr oft brauche. Könnten Sie näher erläutern, warum Sie das wollen? Ich würde es wahrscheinlich als statische Methode in eine Hilfsklasse einordnen:

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

Sie könntenEnumerable.Repeat(default(T), count).ToList() verwenden, aber das wäre aufgrund der Größenänderung des Puffers ineffizient.

Beachten Sie, dass, wenn T ein Referenztyp ist, count Kopien der Referenz gespeichert werden, die für den Parameter value übergeben wurden, sodass alle auf dasselbe Objekt verweisen. Dies kann je nach Anwendungsfall der Fall sein oder auch nicht.

BEARBEITEN: Wie in den Kommentaren erwähnt, können Sie Repeated dazu bringen, die Liste mit einer Schleife zu füllen, wenn Sie dies möchten. Das wäre auch etwas schneller. Persönlich finde ich den Code, der Repeat verwendet, aussagekräftiger und vermute, dass der Leistungsunterschied in der realen Welt irrelevant wäre, aber Ihr Kilometerstand kann variieren.

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

Verwenden Sie den Konstruktor, der ein int ("capacity") als Argument verwendet:

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

EDIT: Ich sollte hinzufügen, dass ich mit Frederik einverstanden bin. Sie verwenden die Liste in einer Weise, die der gesamten Argumentation, die dahinter steht, widerspricht.

EDIT2:

EDIT 2: Was ich gerade schreibe, ist eine Basisklasse, die Standardfunktionalität als Teil eines größeren Frameworks bietet. In der Standardfunktionalität, die ich anbiete, ist die Größe der Liste in erweitert bekannt und daher hätte ich ein Array verwenden können. Ich möchte jedoch jeder Basisklasse die Möglichkeit bieten, sie dynamisch zu erweitern, und entscheide mich daher für eine Liste.

Warum sollte jemand die Größe einer Liste mit allen Nullwerten kennen müssen? Wenn die Liste keine reellen Werte enthält, würde ich eine Länge von 0 erwarten. Die Tatsache, dass dies klobig ist, zeigt jedoch, dass dies gegen die beabsichtigte Verwendung der Klasse verstößt.

24
Ed S.

Warum verwenden Sie eine Liste, wenn Sie sie mit einem festen Wert initialisieren möchten? Ich kann verstehen, dass Sie der Leistung halber eine anfängliche Kapazität festlegen möchten, aber ist dies nicht einer der Vorteile einer Liste gegenüber einem regulären Array, das bei Bedarf erweitert werden kann?

Wenn Sie dies tun:

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

Sie erstellen eine Liste mit einer Kapazität von 100 Ganzzahlen. Dies bedeutet, dass Ihre Liste erst erweitert werden muss, wenn Sie das 101. Element hinzufügen. Das zugrunde liegende Array der Liste wird mit einer Länge von 100 initialisiert.

7

Das Initialisieren des Inhalts einer solchen Liste ist nicht wirklich das, wofür Listen gedacht sind. Listen dienen zum Speichern von Objekten. Wenn Sie bestimmten Objekten bestimmte Zahlen zuordnen möchten, sollten Sie eine Schlüssel-Wert-Paar-Struktur wie eine Hash-Tabelle oder ein Wörterbuch anstelle einer Liste verwenden.

5
Welbog

Erstellen Sie ein Array mit der Anzahl der Elemente, die Sie zuerst möchten, und konvertieren Sie das Array anschließend in eine Liste.

int[] fakeArray = new int[10];

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

Sie scheinen die Notwendigkeit einer positionsbezogenen Zuordnung zu Ihren Daten zu betonen. Wäre ein assoziatives Array nicht passender?

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

Wenn Sie die Liste mit N Elementen mit festem Wert initialisieren möchten:

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

Die akzeptierte Antwort (die mit dem grünen Häkchen) weist ein Problem auf.

Das Problem:

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

Ich empfehle, die obige Zeile zu ändern, um eine Kopie des Objekts zu erstellen. Dazu gibt es viele verschiedene Artikel:

Wenn Sie jedes Element in Ihrer Liste mit dem Standardkonstruktor anstelle von NULL initialisieren möchten, fügen Sie die folgende Methode hinzu:

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

Hinweis zu IList: MSDN IList Remarks : "IList-Implementierungen lassen sich in drei Kategorien unterteilen: schreibgeschützt, mit fester Größe und mit variabler Größe. (...). Für die generische Version dieser Schnittstelle , sehen System.Collections.Generic.IList<T>. "

IList<T> erbt NICHT von IList (aber List<T> implementiert beide IList<T> und IList), aber immer variable Größe . Seit .NET 4.5 haben wir auch IReadOnlyList<T> aber AFAIK, es gibt keine generische Liste mit fester Größe, die genau das ist, wonach Sie suchen.

0
EricBDev

Mit Linq können Sie Ihre Liste geschickt mit einem Standardwert initialisieren. (Ähnlich wie Antwort von David B .)

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

Gehen Sie einen Schritt weiter und initialisieren Sie jede Zeichenfolge mit den unterschiedlichen Werten "Zeichenfolge 1", "Zeichenfolge 2", "Zeichenfolge 3" usw.:

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

Dies ist ein Beispiel, das ich für meinen Unit-Test verwendet habe. Ich habe eine Liste mit Klassenobjekten erstellt. Dann habe ich forloop verwendet, um 'X' Anzahl von Objekten hinzuzufügen, die ich vom Service erwarte. Auf diese Weise können Sie eine Liste für eine bestimmte Größe hinzufügen/initialisieren.

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


    }

Hoffe, ich habe euch geholfen.

0
Abhay Shiro

Eine etwas verspätete, aber erste Lösung, die Sie vorgeschlagen haben, scheint mir weitaus sauberer zu sein: Sie ordnen Speicher nicht zweimal zu. Auch List Constrcutor muss das Array durchlaufen, um es zu kopieren. Es weiß nicht einmal im Voraus, dass nur Null-Elemente enthalten sind.

1. - Zuweisung von N - Schleife N Kosten: 1 * Zuweisung von (N) + N * Schleife_Iteration

2. - Zuweisen von N - Zuweisen von N + loop () Kosten: 2 * Zuweisen von (N) + N * loop_iteration

Die Zuweisung von Listen- und Schleifen ist jedoch möglicherweise schneller, da Listen eine integrierte Klasse ist, C # jedoch sooo ...

0
Victor Drouin