Array versus Liste <T>: Wann welche verwenden?


593
MyClass[] array;
List<MyClass> list;

Was sind die Szenarien, in denen eines dem anderen vorzuziehen ist? Und warum?


9
Arrays sind ziemlich veraltet, wie eine beliebte Diskussion hier zeigt. Auch hier und von unserem Gastgeber im Blog darauf hingewiesen .
Gimel

9
Wenn ich mich nicht irre, hat die Liste <> ein Array als interne Struktur. Wenn das interne Array gefüllt ist, kopiert es einfach den Inhalt in ein Array, das doppelt so groß ist (oder eine andere Konstante mal die aktuelle Größe). en.wikipedia.org/wiki/Dynamic_array
Ykok

Ykok: Was Sie sagen, scheint ungefähr richtig zu sein. Ich habe den Quellcode von List <> hier gefunden .
Carol

19
@gimel Zu argumentieren, dass Arrays veraltet sind, ist vielleicht ein bisschen mutig
awdz9nld

Antworten:


580

In der Realität ist es selten, dass Sie ein Array verwenden möchten. Verwenden List<T>Sie auf jeden Fall eine, wenn Sie Daten hinzufügen / entfernen möchten, da die Größenänderung von Arrays teuer ist. Wenn Sie wissen, dass die Daten eine feste Länge haben und aus einem bestimmten Grund (nach dem Benchmarking) eine Mikrooptimierung durchführen möchten, kann ein Array hilfreich sein.

List<T>bietet viel mehr Funktionalität als ein Array (obwohl LINQ es ein wenig ausgleicht) und ist fast immer die richtige Wahl. Abgesehen von paramsArgumenten natürlich. ;-p

Als Zähler - List<T>ist eindimensional; Wobei Sie rechteckige (usw.) Arrays wie int[,]oder haben, string[,,]aber es gibt andere Möglichkeiten, solche Daten (falls erforderlich) in einem Objektmodell zu modellieren.

Siehe auch:

Das heißt, ich eine machen viel für die Verwendung von Arrays in meinem protobuf-net Projekt; ganz für die Leistung:

  • Es wird viel Bit verschoben, daher byte[]ist a für die Codierung ziemlich wichtig.
  • Ich verwende einen lokalen Rolling byte[]Buffer, den ich fülle, bevor ich ihn an den zugrunde liegenden Stream (und vv) sende. schneller als BufferedStreametc;
  • Es verwendet intern ein Array-basiertes Modell von Objekten ( Foo[]anstatt List<Foo>), da die Größe nach dem Erstellen festgelegt ist und sehr schnell sein muss.

Dies ist jedoch definitiv eine Ausnahme. Für die allgemeine Branchenverarbeitung List<T>gewinnt jedes Mal ein.


8
Das Argument zur Größenänderung ist völlig gültig. Die Benutzer bevorzugen jedoch Listen, auch wenn keine Größenänderung erforderlich ist. Gibt es für diesen letzteren Fall ein solides, logisches Argument oder ist es nichts anderes als "Arrays sind aus der Mode"?
Frederick The Fool

6
"Verwenden Sie auf jeden Fall eine Liste <T>, wenn Sie Daten hinzufügen / entfernen möchten, da die Größenänderung von Arrays teuer ist." List <T> verwendet intern ein Array. Haben Sie an LinkedList <T> gedacht?
Dan-Gph

14
Mehr Funktionen == komplexer == nicht gut, es sei denn, Sie benötigen diese Funktionen. Diese Antwort listet grundsätzlich Gründe auf, warum Arrays besser sind, zieht jedoch die gegenteilige Schlussfolgerung.
Eamon Nerbonne

12
@EamonNerbonne Wenn Sie diese Funktionen nicht verwenden, kann ich so ziemlich garantieren, dass sie Sie nicht verletzen werden ... aber: Die Anzahl der Sammlungen, die niemals mutiert werden müssen, ist meiner Erfahrung nach viel geringer als die, die es sind mutiert
Marc Gravell

7
@MarcGravell: Das hängt von Ihrem Codierungsstil ab. Nach meiner Erfahrung werden praktisch keine Sammlungen jemals mutiert. Das ist; Sammlungen werden aus der Datenbank abgerufen oder aus einer Quelle erstellt. Die weitere Verarbeitung erfolgt jedoch immer durch Neuerstellung einer neuen Sammlung (z. B. Karte / Filter usw.). Selbst wenn eine konzeptionelle Mutation erforderlich ist, ist es am einfachsten, nur eine neue Sammlung zu erstellen. Ich mutiere eine Sammlung immer nur als Leistungsoptimierung, und solche Optimierungen sind in der Regel sehr lokal und setzen die Mutation nicht API-Verbrauchern aus.
Eamon Nerbonne

121

Wirklich nur zu antworten, um einen Link hinzuzufügen, von dem ich überrascht bin, dass er noch nicht erwähnt wurde: Eric's Lipperts Blogeintrag zu "Arrays gelten als etwas schädlich".

Sie können anhand des Titels beurteilen, dass die Verwendung von Sammlungen vorgeschlagen wird, wo immer dies praktikabel ist - aber wie Marc zu Recht betont, gibt es viele Stellen, an denen ein Array wirklich die einzige praktische Lösung ist.


2
Endlich habe ich das über 3 Jahre später gelesen, haha. Guter Artikel damals, guter Artikel heute. :)
Spencer Ruport

21

Ungeachtet der anderen empfohlenen Antworten List<T>sollten Sie bei der Handhabung Arrays verwenden:

  • Bild-Bitmap-Daten
  • andere Low-Level-Datenstrukturen (dh Netzwerkprotokolle)

1
Warum für Netzwerkprotokolle? Möchten Sie hier nicht lieber benutzerdefinierte Strukturen verwenden und ihnen einen speziellen Serializer oder ein explizites Speicherlayout geben? Was spricht darüber hinaus gegen die Verwendung eines List<T>Here-Arrays anstelle eines Byte-Arrays?
Konrad Rudolph

8
@Konrad - Nun, für den Anfang arbeiten Stream.Read und Stream.Write mit Byte [], ebenso wie Encoding etc ...
Marc Gravell

12

Es sei denn, Sie befassen sich wirklich mit der Leistung, und damit meine ich: "Warum verwenden Sie .Net anstelle von C ++?" Sie sollten bei List <> bleiben. Es ist einfacher zu warten und erledigt die ganze Drecksarbeit, die Größe eines Arrays hinter den Kulissen für Sie zu ändern. (Falls erforderlich, ist List <> ziemlich klug bei der Auswahl von Array-Größen, sodass dies normalerweise nicht erforderlich ist.)


15
"Warum verwenden Sie .Net anstelle von C ++?" XNA
Bengt

6

Arrays sollten gegenüber List bevorzugt verwendet werden, wenn die Unveränderlichkeit der Sammlung selbst Teil des Vertrags zwischen dem Kunden- und Anbietercode ist (nicht unbedingt die Unveränderlichkeit der Elemente in der Sammlung) UND wenn IEnumerable nicht geeignet ist.

Zum Beispiel,

var str = "This is a string";
var strChars = str.ToCharArray();  // returns array

Es ist klar, dass die Änderung von "strChars" das ursprüngliche "str" ​​-Objekt nicht mutiert, unabhängig davon, ob der zugrunde liegende Typ von "str" ​​auf Implementierungsebene bekannt ist.

Aber nehmen wir das an

var str = "This is a string";
var strChars = str.ToCharList();  // returns List<char>
strChars.Insert(0, 'X');

In diesem Fall ist aus diesem Code-Snippet allein nicht ersichtlich, ob die Einfügemethode das ursprüngliche "str" ​​-Objekt mutiert oder nicht. Es erfordert Kenntnisse über String auf Implementierungsebene, um diese Bestimmung zu treffen, was den Design by Contract-Ansatz bricht. Im Fall von String ist es keine große Sache, aber es kann in fast jedem anderen Fall eine große Sache sein. Das Festlegen der Liste auf schreibgeschützt hilft zwar, führt jedoch zu Laufzeitfehlern und nicht zur Kompilierungszeit.


Ich bin relativ neu in C #, aber mir ist nicht klar, warum das Zurückgeben einer Liste eine Veränderbarkeit der Originaldaten auf eine Weise nahelegt, die das Zurückgeben eines Arrays nicht zulässt. Ich hätte allerdings gedacht, dass eine Methode, deren Name mit beginnt To, ein Objekt erstellen wird, das die ursprüngliche Instanz nicht ändern kann, im Gegensatz dazu strChars as char[], wenn dies gültig ist, würde dies bedeuten, dass Sie jetzt das ursprüngliche Objekt ändern können.
Tim MB

@TimMB Es gibt die Unveränderlichkeit der Sammlung (keine Elemente können hinzugefügt oder entfernt werden) und die Unveränderlichkeit der Elemente in der Sammlung. Ich bezog mich auf Letzteres, während Sie wahrscheinlich die beiden miteinander verbinden. Durch die Rückgabe eines Arrays wird sichergestellt, dass der Client keine Elemente hinzufügen / entfernen kann. In diesem Fall wird das Array neu zugewiesen, und es wird sichergestellt, dass das Original nicht beeinträchtigt wird. Bei der Rückgabe einer Liste werden keine solchen Zusicherungen gemacht und das Original könnte beeinträchtigt werden (abhängig von der Implementierung). Das Ändern von Elementen in der Sammlung (ob Array oder Liste) kann sich auf das Original auswirken, wenn der Elementtyp keine Struktur ist.
Herman Schönfeld

Danke für die Abklärung. Ich bin immer noch verwirrt (wahrscheinlich, weil ich aus der C ++ - Welt komme). Wenn strintern ein Array verwendet wird und ToCharArrayein Verweis auf dieses Array zurückgegeben wird, kann der Client strdurch Ändern der Elemente dieses Arrays mutieren , auch wenn die Größe fest bleibt. Sie schreiben jedoch: "Es ist klar, dass eine Änderung von" strChars "das ursprüngliche" str "-Objekt nicht mutiert." Was fehlt mir hier? Soweit ich sehen kann, kann der Client in beiden Fällen Zugriff auf die interne Darstellung haben, und dies würde unabhängig vom Typ eine Mutation ermöglichen.
Tim MB

3

Wenn ich genau weiß, wie viele Elemente ich benötige, sagen wir, ich benötige 5 Elemente und immer nur 5 Elemente, dann verwende ich ein Array. Ansonsten benutze ich einfach eine Liste <T>.


1
Warum würden Sie keine Liste <T> verwenden, wenn Sie die Anzahl der Elemente kennen?
Oliver

3

In den meisten ListFällen würde die Verwendung von a ausreichen. A Listverwendet ein internes Array, um seine Daten zu verarbeiten, und ändert automatisch die Größe des Arrays, wenn mehr Elemente zur Listaktuellen Kapazität hinzugefügt werden. Dies macht die Verwendung einfacher als bei einem Array, bei dem Sie die Kapazität im Voraus kennen müssen.

Weitere Informationen zu Listen in C # oder zum Dekompilieren finden Sie unter http://msdn.microsoft.com/en-us/library/ms379570(v=vs.80).aspx#datastructures20_1_topic5System.Collections.Generic.List<T> .

Wenn Sie mehrdimensionale Daten benötigen (z. B. mithilfe einer Matrix oder in der Grafikprogrammierung), würden Sie wahrscheinlich stattdessen eine arrayverwenden.

Wie immer, wenn Speicher oder Leistung ein Problem sind, messen Sie es! Andernfalls könnten Sie falsche Annahmen über den Code treffen.


1
Hallo, können Sie erklären, warum "Die Suchzeit einer Liste wäre O (n)" wahr ist? Soweit ich weiß, verwendet List <T> ein Array hinter den Kulissen.
Libelle

1
@ Dragonfly du hast vollkommen recht. Quelle . Zu der Zeit habe ich angenommen, dass die Implementierung Zeiger verwendet, aber seitdem habe ich etwas anderes gelernt. Über den obigen Link: 'Das Abrufen des Werts dieser Eigenschaft ist eine O (1) -Operation. Das Festlegen der Eigenschaft ist ebenfalls eine O (1) -Operation. '
Sune Rievers

2

Arrays Vs. Listen sind ein klassisches Problem zwischen Wartbarkeit und Leistung. Die Faustregel, die fast alle Entwickler befolgen, lautet, dass Sie für beide fotografieren sollten. Wenn sie jedoch in Konflikt geraten, wählen Sie die Wartbarkeit gegenüber der Leistung. Die Ausnahme von dieser Regel ist, wenn sich die Leistung bereits als Problem erwiesen hat. Wenn Sie dieses Prinzip in Arrays Vs. Listen, dann erhalten Sie Folgendes:

Verwenden Sie stark typisierte Listen, bis Sie auf Leistungsprobleme stoßen. Wenn Sie auf ein Leistungsproblem stoßen, entscheiden Sie, ob ein Ausfall auf Arrays Ihrer Lösung mehr Leistung bringt, als dass dies Ihre Lösung in Bezug auf die Wartung beeinträchtigt.


1

Eine andere Situation, die noch nicht erwähnt wurde, besteht darin, dass eine große Anzahl von Elementen vorhanden ist, von denen jedes aus einem festen Bündel verwandter, aber unabhängiger Variablen besteht, die zusammenkleben (z. B. die Koordinaten eines Punkts oder die Eckpunkte eines 3D-Dreiecks). Ein Array von exponierten Feldstrukturen ermöglicht es, seine Elemente effizient "an Ort und Stelle" zu modifizieren - was mit keinem anderen Sammlungstyp möglich ist. Da ein Array von Strukturen seine Elemente nacheinander im RAM hält, können sequentielle Zugriffe auf Array-Elemente sehr schnell erfolgen. In Situationen, in denen Code viele aufeinanderfolgende Durchläufe durch ein Array ausführen muss, kann ein Array von Strukturen ein Array oder eine andere Sammlung von Klassenobjektreferenzen um den Faktor 2: 1 übertreffen. des Weiteren,

Obwohl die Größe von Arrays nicht geändert werden kann, ist es nicht schwierig, dass Code eine Array-Referenz zusammen mit der Anzahl der verwendeten Elemente speichert und das Array nach Bedarf durch ein größeres ersetzt. Alternativ könnte man leicht Code für einen Typ schreiben, der sich ähnlich wie a List<T>verhält, aber seinen Hintergrundspeicher freigibt, so dass man entweder MyPoints.Add(nextPoint);oder sagen kann MyPoints.Items[23].X += 5;. Beachten Sie, dass letzteres nicht unbedingt eine Ausnahme auslösen würde, wenn Code versuchen würde, über das Ende der Liste hinaus zuzugreifen, aber die Verwendung ansonsten konzeptionell ziemlich ähnlich wäre List<T>.


Was Sie beschrieben haben, ist eine Liste <>. Es gibt einen Indexer, mit dem Sie direkt auf das zugrunde liegende Array zugreifen können, und die Liste <> behält die Größe für Sie bei.
Carl

@Carl: Gegeben Point[] arr;, z arr[3].x+=q;. B. kann Code sagen, z . Mit zB List<Point> listmüsste man stattdessen sagen Point temp=list[3]; temp.x+=q; list[3]=temp;. Es wäre hilfreich, wenn List<T>eine Methode hätte Update<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params). und Compiler könnten sich list[3].x+=q;in verwandeln, {list.Update(3, (ref int value, ref int param)=>value+=param, ref q);aber es gibt keine solche Funktion.
Supercat

Gute Nachrichten. Es klappt. list[0].X += 3;fügt der X-Eigenschaft des ersten Elements der Liste 3 hinzu. Und listist eine List<Point>und Pointist eine Klasse mit X- und Y-Eigenschaften
Carl

1

Listen in .NET sind Wrapper über Arrays und verwenden ein Array intern. Die zeitliche Komplexität von Operationen an Listen ist dieselbe wie bei Arrays, jedoch ist der Aufwand mit all den zusätzlichen Funktionen / der Benutzerfreundlichkeit von Listen (z. B. der automatischen Größenänderung und den mit der Listenklasse gelieferten Methoden) etwas höher. Ich würde in jedem Fall die Verwendung von Listen empfehlen, es sei denn, es gibt einen zwingenden Grund, dies nicht zu tun, z. B. wenn Sie extrem optimierten Code schreiben müssen oder mit anderem Code arbeiten, der auf Arrays basiert.


0

Anstatt die Funktionen der einzelnen Datentypen zu vergleichen, ist die pragmatischste Antwort meiner Meinung nach: "Die Unterschiede sind wahrscheinlich nicht so wichtig für das, was Sie erreichen müssen, zumal beide implementiert IEnumerablesind. Befolgen Sie daher die gängigen Konventionen und verwenden Sie a ListBis Sie einen Grund haben, dies nicht zu tun, haben Sie wahrscheinlich Ihren Grund, ein Array über a zu verwenden List. "

Die meiste Zeit in verwaltetem Code möchten Sie, dass Sammlungen so einfach wie möglich zu bearbeiten sind, anstatt sich über Mikrooptimierungen Gedanken zu machen.


0

Sie mögen unbeliebt sein, aber ich bin ein Fan von Arrays in Spielprojekten. - Die Iterationsgeschwindigkeit kann in einigen Fällen wichtig sein. Foreach auf einem Array hat erheblich weniger Overhead, wenn Sie nicht viel pro Element tun. - Das Hinzufügen und Entfernen ist mit Hilfsfunktionen nicht so schwierig. - Es ist langsamer, aber in Fällen, in denen Sie es nur einmal erstellen Es spielt möglicherweise keine Rolle. - In den meisten Fällen wird weniger zusätzlicher Speicher verschwendet (nur bei Arrays von Strukturen von großer Bedeutung). - Etwas weniger Müll und Zeiger und Zeigerjagd

Davon abgesehen verwende ich List in der Praxis weitaus häufiger als Arrays, aber sie haben jeweils ihren Platz.

Es wäre schön, wenn List einen eingebauten Typ hätte, damit sie den Wrapper- und Aufzählungsaufwand optimieren können.


0

Das Auffüllen einer Liste ist einfacher als ein Array. Für Arrays müssen Sie die genaue Länge der Daten kennen, für Listen kann die Datengröße jedoch beliebig sein. Außerdem können Sie eine Liste in ein Array konvertieren.

List<URLDTO> urls = new List<URLDTO>();

urls.Add(new URLDTO() {
    key = "wiki",
    url = "https://...",
});

urls.Add(new URLDTO()
{
    key = "url",
    url = "http://...",
});

urls.Add(new URLDTO()
{
    key = "dir",
    url = "https://...",
});

// convert a list into an array: URLDTO[]
return urls.ToArray();

0

Da niemand erwähnt: In C # ist ein Array eine Liste. MyClass[]und List<MyClass>beide implementieren IList<MyClass>. (zB void Foo(IList<int> foo)kann wie Foo(new[] { 1, 2, 3 })oder genannt werden Foo(new List<int> { 1, 2, 3 }))

Wenn Sie also eine Methode schreiben, die a List<MyClass>als Argument akzeptiert , aber nur eine Teilmenge von Funktionen verwendet, möchten Sie diese möglicherweise aus Gründen der Benutzerfreundlichkeit als deklarieren IList<MyClass>.

Einzelheiten:


"In C # ist ein Array eine Liste" Das stimmt nicht. Ein Array ist kein Array List, sondern implementiert nur die IListSchnittstelle.
Rufus L

-1

Dies hängt vollständig von den Kontexten ab, in denen die Datenstruktur benötigt wird. Wenn Sie beispielsweise mithilfe von List Elemente erstellen, die von anderen Funktionen oder Diensten verwendet werden sollen, ist dies der perfekte Weg, um dies zu erreichen.

Wenn Sie nun eine Liste von Elementen haben und diese nur anzeigen möchten, ist beispielsweise auf einem Webseitenarray der Container, den Sie verwenden müssen.


1
Wenn Sie eine Liste mit Elementen haben und diese nur anzeigen möchten, was ist dann falsch daran, nur die Liste zu verwenden, die Sie bereits haben? Was würde ein Array hier bieten?
Marc Gravell

1
Und für "Erstellen von Elementen, die von anderen Funktionen oder Diensten verwendet werden sollen" würde ich eigentlich einen Iteratorblock mit bevorzugen IEnumerable<T>- dann kann ich Objekte streamen, anstatt sie zu puffern.
Marc Gravell

-1

Erwähnenswert ist die Fähigkeit, vor Ort zu gießen.

interface IWork { }
class Foo : IWork { }

void Test( )
{
    List<Foo> bb = new List<Foo>( );
    // Error: CS0029 Cannot implicitly convert type 'System.Collections.Generic.List<Foo>' to 'System.Collections.Generic.List<IWork>'
    List<IWork> cc = bb; 

    Foo[] bbb = new Foo[4];
    // Fine
    IWork[] ccc = bbb;
}

So Array bietet Bit wenig mehr Flexibilität , wenn sie in Rückgabetyp oder ein Argument für Funktionen verwendet.

IWork[] GetAllWorks( )
{
    List<Foo> fooWorks = new List<Foo>( );
    return fooWorks.ToArray( ); // Fine
}

void ExecuteWorks( IWork[] works ) { } // Also accept Foo[]

Sie sollten beachten , dass Arrays Varianz ist gebrochen .
Mcarton
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.