MyClass[] array;
List<MyClass> list;
Quali sono gli scenari in cui uno è preferibile all'altro? E perché?
MyClass[] array;
List<MyClass> list;
Quali sono gli scenari in cui uno è preferibile all'altro? E perché?
Risposte:
È raro, in realtà, che tu voglia usare un array. Sicuramente utilizzare List<T>
ogni volta che si desidera aggiungere / rimuovere dati, poiché il ridimensionamento delle matrici è costoso. Se sai che i dati hanno una lunghezza fissa e vuoi micro-ottimizzare per qualche motivo molto specifico (dopo il benchmarking), allora un array può essere utile.
List<T>
offre molte più funzionalità di un array (anche se LINQ ne migliora un po ') ed è quasi sempre la scelta giusta. Tranne gli params
argomenti, ovviamente. ;-P
Come contatore - List<T>
è monodimensionale; dove-come avete matrici rettangolari (ecc.) come int[,]
o string[,,]
- ma ci sono altri modi per modellare tali dati (se necessario) in un modello a oggetti.
Guarda anche:
Detto questo, uso molto gli array nel mio progetto protobuf-net ; interamente per prestazioni:
byte[]
è praticamente essenziale per la codifica;byte[]
buffer di rotolamento locale che riempio prima di inviarlo al flusso sottostante (e vv); più veloce di BufferedStream
ecc .;Foo[]
piuttosto che List<Foo>
), poiché la dimensione viene fissata una volta creata e deve essere molto veloce.Ma questa è sicuramente un'eccezione; per l'elaborazione generale della linea di business, List<T>
ogni volta vince.
In realtà non ho ancora detto di aggiungere un link che mi sorprende: il post sul blog di Eric Lippert su "Le matrici sono state considerate piuttosto dannose".
Puoi giudicare dal titolo che suggerisce di usare le collezioni ovunque sia pratico, ma come giustamente sottolinea Marc, ci sono molti posti in cui un array è davvero l'unica soluzione pratica.
Nonostante le altre risposte consigliate List<T>
, ti consigliamo di utilizzare le matrici durante la gestione:
List<T>
di un array qui anziché di un byte?
A meno che non ti interessi davvero delle prestazioni, e con questo intendo "Perché stai usando .Net invece di C ++?" dovresti rimanere con Elenco <>. È più facile da mantenere e fa tutto il lavoro sporco di ridimensionare un array dietro le quinte per te. (Se necessario, Elenco <> è piuttosto intelligente nella scelta delle dimensioni dell'array, quindi non è necessario in genere.)
Le matrici dovrebbero essere usate in preferenza all'Elenco quando l'immutabilità della raccolta stessa fa parte del contratto tra il codice cliente e fornitore (non necessariamente l'immutabilità degli articoli all'interno della raccolta) E quando IEnumerable non è adatto.
Per esempio,
var str = "This is a string";
var strChars = str.ToCharArray(); // returns array
È chiaro che la modifica di "strChars" non muta l'oggetto originale "str", indipendentemente dalla conoscenza a livello di implementazione del tipo sottostante di "str".
Ma supponiamo che
var str = "This is a string";
var strChars = str.ToCharList(); // returns List<char>
strChars.Insert(0, 'X');
In questo caso, non è chiaro solo dallo snippet di codice se il metodo insert inserirà o non muterà l'oggetto "str" originale. Richiede una conoscenza a livello di implementazione di String per fare quella determinazione, che rompe l'approccio Design by Contract. Nel caso di String, non è un grosso problema, ma può essere un grosso problema in quasi tutti gli altri casi. L'impostazione dell'elenco su sola lettura aiuta ma provoca errori di runtime, non di compilazione.
To
quello di creare un oggetto che non ha la possibilità di modificare l'istanza originale, al contrario del strChars as char[]
quale se valido suggerirebbe che ora è possibile modificare l'oggetto originale.
str
utilizza internamente un array e ToCharArray
restituisce un riferimento a questo array, il client può mutare str
modificando gli elementi di tale array, anche se la dimensione rimane fissa. Eppure scrivi 'È chiaro che la modifica di "strChars" non muterà l'oggetto "str" originale'. Cosa mi sto perdendo qui? Da quello che posso vedere, in entrambi i casi il cliente potrebbe avere accesso alla rappresentazione interna e, indipendentemente dal tipo, ciò consentirebbe una mutazione di qualche tipo.
Il più delle volte, usare a List
sarebbe sufficiente. A List
utilizza un array interno per gestire i suoi dati e ridimensiona automaticamente l'array quando aggiunge più elementi List
rispetto alla sua capacità corrente, il che lo rende più facile da usare rispetto a un array, dove è necessario conoscere in anticipo la capacità.
Vedere http://msdn.microsoft.com/en-us/library/ms379570(v=vs.80).aspx#datastructures20_1_topic5 per ulteriori informazioni sugli elenchi in C # o semplicemente decompilare System.Collections.Generic.List<T>
.
Se hai bisogno di dati multidimensionali (ad esempio usando una matrice o nella programmazione grafica), probabilmente andresti con un array
invece.
Come sempre, se la memoria o le prestazioni sono un problema, misuralo! Altrimenti potresti fare false ipotesi sul codice.
Array vs. Gli elenchi rappresentano un classico problema di manutenibilità e prestazioni. La regola empirica che quasi tutti gli sviluppatori seguono è che dovresti sparare per entrambi, ma quando entrano in conflitto, scegli la manutenibilità piuttosto che le prestazioni. L'eccezione a tale regola è quando le prestazioni hanno già dimostrato di essere un problema. Se porti questo principio ad Arrays vs. Elenchi, quindi quello che ottieni è questo:
Utilizzare elenchi fortemente tipizzati fino a quando non si verificano problemi di prestazioni. Se si riscontra un problema di prestazioni, prendere una decisione se abbandonare gli array andrà a beneficio della soluzione con prestazioni più di quanto non pregiudichi la soluzione in termini di manutenzione.
Un'altra situazione non ancora menzionata è quando si avrà un gran numero di elementi, ognuno dei quali è costituito da un insieme fisso di variabili correlate ma indipendenti unite (ad esempio le coordinate di un punto o i vertici di un triangolo 3d). Una serie di strutture a campo esposto permetterà ai suoi elementi di essere efficacemente modificati "sul posto", cosa che non è possibile con nessun altro tipo di raccolta. Poiché una matrice di strutture mantiene i suoi elementi consecutivamente nella RAM, gli accessi sequenziali agli elementi della matrice possono essere molto veloci. In situazioni in cui il codice dovrà effettuare molti passaggi sequenziali attraverso un array, un array di strutture può sovraperformare un array o altra raccolta di riferimenti a oggetti di classe di un fattore 2: 1; ulteriore,
Sebbene le matrici non siano ridimensionabili, non è difficile che il codice memorizzi un riferimento di matrice insieme al numero di elementi in uso e sostituisca la matrice con una più grande come richiesto. In alternativa, si potrebbe facilmente scrivere codice per un tipo che si è comportato in modo molto simile a un List<T>
ma ha esposto il suo archivio di supporto, consentendo così di dire uno MyPoints.Add(nextPoint);
o MyPoints.Items[23].X += 5;
. Si noti che quest'ultimo non costituirebbe necessariamente un'eccezione se il codice tentasse di accedere oltre la fine dell'elenco, ma l'utilizzo sarebbe altrimenti concettualmente abbastanza simile a List<T>
.
Point[] arr;
, è possibile dire il codice, ad es arr[3].x+=q;
. Usando ad esempio List<Point> list
, sarebbe invece necessario dire Point temp=list[3]; temp.x+=q; list[3]=temp;
. Sarebbe utile se List<T>
avesse un metodo Update<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params)
. e i compilatori potrebbero trasformarsi list[3].x+=q;
in {list.Update(3, (ref int value, ref int param)=>value+=param, ref q);
ma tale funzionalità non esiste.
list[0].X += 3;
aggiungerà 3 alla proprietà X del primo elemento dell'elenco. Ed list
è una List<Point>
ed Point
è una classe con proprietà X e Y
Gli elenchi in .NET sono wrapper su array e usano un array internamente. La complessità temporale delle operazioni sugli elenchi è la stessa di quella degli array, tuttavia c'è un po 'più di sovraccarico con tutta la funzionalità / facilità d'uso degli elenchi (come il ridimensionamento automatico e i metodi forniti con la classe list). Praticamente, consiglierei di utilizzare gli elenchi in tutti i casi, a meno che non ci sia un motivo convincente per non farlo, ad esempio se è necessario scrivere un codice estremamente ottimizzato o lavorare con un altro codice costruito attorno agli array.
Piuttosto che passare attraverso un confronto delle caratteristiche di ciascun tipo di dati, penso che la risposta più pragmatica sia "le differenze probabilmente non sono così importanti per quello che devi realizzare, specialmente perché entrambi implementano IEnumerable
, quindi segui le convenzioni popolari e usa un List
fino a quando non avrai un motivo per non farlo, a quel punto probabilmente avrai il tuo motivo per usare un array su un List
".
La maggior parte delle volte nel codice gestito vorrai preferire che le raccolte siano il più facili da lavorare rispetto a preoccuparti delle micro-ottimizzazioni.
Potrebbero essere impopolari, ma io sono un fan di Array nei progetti di gioco. - La velocità di iterazione può essere importante in alcuni casi, foreach su un array ha un sovraccarico significativamente inferiore se non stai facendo molto per elemento - Aggiungere e rimuovere non è così difficile con le funzioni di supporto - È più lento, ma nei casi in cui lo costruisci solo una volta potrebbe non importare - Nella maggior parte dei casi, si spreca meno memoria extra (davvero significativo solo con le matrici di strutture) - Leggermente meno spazzatura e puntatori e inseguimento dei puntatori
Detto questo, uso List molto più spesso degli array in pratica, ma ognuno di essi ha il suo posto.
Sarebbe bello se List fosse un tipo incorporato in modo che potessero ottimizzare il sovraccarico del wrapper e dell'enumerazione.
Il popolamento di un elenco è più semplice di un array. Per gli array, è necessario conoscere la lunghezza esatta dei dati, ma per gli elenchi, la dimensione dei dati può essere qualsiasi. Inoltre, puoi convertire un elenco in un array.
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();
Poiché nessuno menziona: in C #, un array è un elenco. MyClass[]
ed List<MyClass>
entrambi implementano IList<MyClass>
. (es. void Foo(IList<int> foo)
può essere chiamato come Foo(new[] { 1, 2, 3 })
o Foo(new List<int> { 1, 2, 3 })
)
Quindi, se stai scrivendo un metodo che accetta un List<MyClass>
come argomento, ma utilizza solo un sottoinsieme di funzionalità, potresti voler dichiararlo come IList<MyClass>
invece per comodità dei chiamanti.
Dettagli:
List
, implementa solo l' IList
interfaccia.
Dipende completamente dai contesti in cui è necessaria la struttura dei dati. Ad esempio, se si stanno creando elementi da utilizzare per altre funzioni o servizi utilizzando Elenco è il modo perfetto per realizzarlo.
Ora, se si dispone di un elenco di elementi e si desidera solo visualizzarli, dire su un array di pagine Web è il contenitore che è necessario utilizzare.
IEnumerable<T>
- quindi posso eseguire lo streaming di oggetti piuttosto che memorizzarli in buffer.
Vale la pena menzionare la capacità di fare casting in loco.
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;
}
Quindi Array offre un po 'più di flessibilità quando viene utilizzato nel tipo restituito o nell'argomento per le funzioni.
IWork[] GetAllWorks( )
{
List<Foo> fooWorks = new List<Foo>( );
return fooWorks.ToArray( ); // Fine
}
void ExecuteWorks( IWork[] works ) { } // Also accept Foo[]