C'è un motivo per utilizzare un array quando sono disponibili elenchi? [chiuso]

27

Sembra che List<T> in C # possa fare tutto ciò che un array può fare e altro, e sembra altrettanto efficiente in termini di memoria e prestazioni come array.

Quindi, perché dovrei voler usare un array?

Ovviamente non sto chiedendo di casi in cui un'API o un altro vincolo esterno (ovvero la funzione Main) mi impone di utilizzare un array ... Sto solo chiedendo di creare nuove strutture di dati nel mio codice.

    
posta JoelFan 10.12.2014 - 16:29
fonte

14 risposte

24

Lo stesso motivo per cui non guido un camion quando vado al lavoro. Non uso qualcosa che non userò le funzionalità di.

Prima di tutto un array è un costrutto primitivo, quindi un array è più veloce e più efficiente di un elenco < > di sicuro, quindi la tua argomentazione non è vera. Array è anche disponibile ovunque e conosciuto dagli sviluppatori che utilizzano linguaggi e piattaforme differenti.

Il motivo più importante per cui utilizzo una matrice invece di una lista < > significa che i dati sono lunghezza fissa . Se non aggiungo o rimuovo alcun elemento da quella raccolta di dati, voglio assicurarmi che il tipo lo rispecchi.

Un'altra cosa è dire che stai implementando una nuova struttura dati e hai letto alcuni articoli a riguardo. Ora, mentre si implementano algoritmi specifici, non è sempre possibile fare affidamento sull'implementazione di un tipo di un altro che è di uso generale. Cambia da .NET a Mono e anche tra diverse versioni del framework.

E a volte è più facile portare un pezzo di codice che utilizza una matrice invece di un tipo dipendente dal framework.

    
risposta data 10.12.2014 - 19:41
fonte
15

So why would I ever want to use an array?

Raramente , avrai uno scenario in cui conosci che hai bisogno di un numero fisso di elementi. Dal punto di vista del design, questo dovrebbe essere evitato. Se hai bisogno di 3 cose, la natura del business significa che avrai molto spesso bisogno di 4 nella prossima versione.

Tuttavia, quando questo raro scenario si verifica effettivamente, è utile utilizzare un array per rafforzare l'invarianza di dimensione fissa. Fornisce un segnale ad altri programmatori che è di dimensioni fisse e aiuta a prevenire l'uso improprio in cui qualcuno aggiunge o rimuove un elemento - rompendo le aspettative altrove nel codice.

    
risposta data 10.12.2014 - 16:38
fonte
13

Hai bisogno di matrici per gestire la tua collezione di strutture mutevoli , naturalmente, e cosa faremmo senza di questi.

struct EvilMutableStruct { public double X; } // don't do this

EvilMutableStruct[] myArray = new EvilMutableStruct[1];
myArray[0] = new EvilMutableStruct()
myArray[0].X = 1; // works, this modifies the original struct

List<EvilMutableStruct> myList = new List<EvilMutableStruct>();
myList.Add(new EvilMutableStruct());
myList[0].X = 1; // does not work, the List will return a *copy* of the struct

(si noti che potrebbero esserci alcuni casi in cui una matrice di strutture mutevoli è desiderabile, ma solitamente questo comportamento differente delle strutture mutabili all'interno di array rispetto ad altre collezioni è una fonte di errori che dovrebbero essere evitati)

Più seriamente, hai bisogno di un array se vuoi passare un elemento per riferimento . cioè.

Interlocked.Increment(ref myArray[i]);  // works
Interlocked.Increment(ref myList[i]);   // does not work, you can't pass a property by reference

Può essere utile per codice thread-safe senza blocco.

Hai bisogno di un array se desideri inizializzare in modo rapido ed efficiente la tua raccolta a dimensione fissa con il valore predefinito .

double[] myArray = new double[1000]; // contains 1000 '0' values
                                     // without further initialisation

List<double> myList = new List<double>(1000) // internally contains 1000 '0' values, 
                                             // since List uses an array as backing storage, 
                                             // but you cannot access those
for (int i =0; i<1000; i++) myList.Add(0);   // slow and inelegant

(nota che sarebbe possibile implementare un costruttore per List che faccia lo stesso, è solo che c # non offre questa funzionalità)

hai bisogno di un array se vuoi copiare efficientemente parti della collezione

Array.Copy(array1, index1, array2, index2, length) // can't get any faster than this

double[,] array2d = new double[10,100];
double[] arraySerialized = new double[10*100];
Array.Copy(array2d, 0, arraySerialized, 0, arraySerialized.Length);
// even works for different dimensions

(di nuovo, questo è qualcosa che potrebbe essere implementato anche per List, ma questa funzionalità non esiste in c #)

    
risposta data 10.12.2014 - 19:40
fonte
8

La tua domanda ha in realtà già risposta prima .

and seems also just as efficient in memory and performance as an array.

Non lo è. Dalla domanda che ho collegato:

List/foreach:  3054ms (589725196)
Array/foreach: 1860ms (589725196)

Gli array sono due volte più veloci in alcuni casi importanti. Sono certo che l'utilizzo della memoria differisce anche non banalmente.

Dato che la premessa principale della tua domanda è quindi sconfitta, presumo che questo risponda alla tua domanda. In aggiunta a ciò, a volte gli array sono forzati da Win32 API, dallo shader della tua GPU o da qualche altra libreria non DotNet.

Anche all'interno di DotNet, alcuni metodi consumano e / o restituiscono array (come String.Split ). Il che significa che ora devi mangiare il costo di chiamare ToList e ToArray per tutto il tempo, oppure devi conformare e usare l'array, possibilmente continuando il ciclo propagandolo a utenti poveri di tuo codice.

Altre domande e risposte su Stack Overflow su questo argomento:

risposta data 11.12.2014 - 02:23
fonte
3

Oltre ai motivi elencati in altre risposte, la matrice letterale richiede meno caratteri per dichiarare:

var array = new [] { "A", "B", "C" };
var list = new List<string>() { "A", "B", "C" };

L'uso dell'array invece di List rende il codice un po 'più breve e solo un po' più leggibile nei casi in cui (1) devi passare qualsiasi IEnumerable<T> literal o (2) dove l'altra funzionalità di List non lo fa Non importa e hai bisogno di usare un po 'di tipo letterale.

L'ho fatto occasionalmente nei test unitari.

    
risposta data 10.12.2014 - 18:17
fonte
3

Questo è strettamente da una prospettiva OO.

Anche se non riesco a pensare a un motivo per passare solo un array in giro, posso certamente vedere situazioni in cui una rappresentazione di matrice interna alla classe è probabilmente la scelta migliore.

Sebbene esistano altre opzioni che offrono caratteristiche simili, nessuna sembra intuitiva come una matrice per problemi relativi alle permutazioni di elaborazione, nidificate per cicli, rappresentazione di matrici, bitmap e algoritmi di interleaving dei dati.

Esiste un numero considerevole di campi scientifici che si basano ampiamente su Matrix Matematica. (ad esempio elaborazione di immagini, correzione di errori di dati, elaborazione di segnali digitali, una risma di problemi di matematica applicata). La maggior parte degli algoritmi in questi campi sono scritti in termini di utilizzo di matrici / matrici multidimensionali. Quindi sarebbe più naturale implementare gli algoritmi così come sono definiti piuttosto che renderli più "software" amichevoli a scapito della perdita dei legami diretti con i documenti su cui si basano gli algoritmi.

Come ho già detto, in questi casi è probabile che si riesca a utilizzare le liste, ma questo aggiunge un ulteriore livello di complessità in aggiunta a quelli che sono già algoritmi complessi.

    
risposta data 10.12.2014 - 20:16
fonte
3

Questo vale per altre lingue che hanno anche elenchi (come Java o Visual Basic). Vi sono casi in cui è necessario utilizzare un array perché un metodo restituisce un array anziché un elenco.

In un programma reale, non penso che un array verrà usato molto spesso, ma a volte sai che i dati saranno di dimensioni fisse e ti piace il piccolo guadagno di prestazioni ottenuto dall'uso di un array. La micro-ottimizzazione sarebbe un valido motivo, proprio come un metodo che restituisce una lista, o la necessità di una infrastruttura multidimensionale.

    
risposta data 10.12.2014 - 16:34
fonte
1

Bene, ho trovato un uso per gli array in un gioco che ho scritto. L'ho usato per creare un sistema di inventario con un numero fisso di slot. Ciò ha avuto diversi vantaggi:

  1. Sapevo esattamente quanti slot di inventario aperti avevano gli oggetti di gioco guardando e vedendo quali slot erano nulli.
  2. Sapevo esattamente in quale indice si trovava ciascun elemento.
  3. Era ancora un array "tipizzato" (Item [] Inventory), quindi ho potuto aggiungere / rimuovere gli oggetti di tipo "Item".

Ho pensato che se avessi mai avuto bisogno di "aumentare" la dimensione dell'inventario, avrei potuto farlo trasferendo i vecchi elementi nel nuovo array, ma poiché l'inventario era stato riparato dallo spazio dello schermo e non avevo bisogno di dinamicamente renderlo più grande / più piccolo, ha funzionato bene per lo scopo per il quale lo stavo usando.

    
risposta data 10.12.2014 - 20:27
fonte
1

Se stai attraversando tutti gli elementi di una lista, allora no, una matrice non è necessaria, la selezione 'successiva' o arbitraria 'senza sostituzione' andrà bene.

Ma se il tuo algoritmo ha bisogno dell'accesso casuale agli elementi nella raccolta, allora, sì, è necessario un array.

Questo è in qualche modo analogo a "è necessario goto?". In un linguaggio moderno ragionevole non è affatto necessario. Ma se togli le astrazioni, ad un certo punto, questo è tutto ciò che è effettivamente disponibile per te, cioè, l'unico modo per implementare queste astrazioni è con la funzione 'inutile'. (Naturalmente, l'analogia non è perfetta, non penso che nessuno dica che gli array sono una cattiva pratica di programmazione, sono facili da capire e da pensare).

    
risposta data 10.12.2014 - 20:32
fonte
0

Compatibilità legacy.

Tutta la forma di esperienza personale:

I programmatori legacy - il mio collega usa array dappertutto, ha fatto per oltre 30 anni, buona fortuna cambiando idea con le tue nuove idee confuse.

Codice legacy - foo (array bar []) sicuramente puoi usare una funzione list / vector / collection toarray ma se non usi nessuna di queste funzioni aggiuntive è più facile usare una matrice per iniziare, spesso più leggibile senza il type switching.

Capo legacy - il mio capo era un buon programmatore prima di entrare in gestione molti anni fa e pensa ancora di essere aggiornato, "usare gli array" può terminare un incontro, spiegando che cos'è una raccolta che può costare a tutti pranzare.

    
risposta data 10.12.2014 - 18:01
fonte
0

1) Non esiste una versione multidimensionale di List. Se i tuoi dati hanno più di una dimensione, sarà molto inefficiente usare gli elenchi.

2) Quando si ha a che fare con un gran numero di piccoli tipi di dati (diciamo, una mappa in cui tutto ciò che si ha è un byte per il tipo di terreno) ci possono essere notevoli differenze di prestazioni dovute alla memorizzazione nella cache. La versione dell'array carica diversi elementi per lettura di memoria, la versione di elenco ne carica solo uno. Inoltre, la versione dell'array contiene più celle della cache quante sono le versioni della lista: se si elaborano ripetutamente i dati, questa può fare una grande differenza se la versione dell'array si adatta alla cache ma la versione dell'elenco non lo fa. p>

Per un caso estremo, considera Minecraft. (Sì, non è scritto in C #. Si applica lo stesso motivo.)

    
risposta data 10.12.2014 - 21:40
fonte
0

Una matrice di 100 elementi di qualche tipo T incapsula 100 variabili indipendenti di tipo T. Se T sembra essere un tipo di valore che ha un campo pubblico mutabile di tipo Q e uno di tipo R, allora ogni elemento dell'array sarà incapsulare variabili indipendenti dei tipi Q e R. L'array nel suo complesso incapsulerà quindi 100 variabili indipendenti di tipo Q e 100 variabili indipendenti di tipo R; è possibile accedere a ciascuna di queste variabili singolarmente senza influire su altre. Nessun tipo di raccolta diverso dagli array può consentire l'utilizzo dei campi delle strutture come variabili indipendenti.

Se T è successo invece di essere un tipo di classe con campi mutabili pubblici di tipo Q e R, ogni elemento dell'array detiene il riferimento solo , in qualsiasi parte dell'universo, a un'istanza di T , e se nessuno degli elementi dell'array sarà mai modificato per identificare un oggetto al quale esiste un riferimento esterno, allora l'array incapsulerà effettivamente 100 variabili indipendenti di tipo Q e 100 variabili indipendenti di tipo R. Altri tipi di raccolta possono imitare il comportamento di tale array, ma se l'unico scopo dell'array è quello di incapsulare 100 variabili di tipo Q e 100 di tipo R, incapsulare ogni coppia di variabili nel proprio oggetto di classe è un modo costoso per farlo. Inoltre, l'uso di una matrice o di una raccolta di tipi di classe mutabili crea la possibilità che le variabili identificate dagli elementi dell'array non siano indipendenti .

Se un tipo dovrebbe comportarsi come un qualche tipo di oggetto, allora dovrebbe essere un tipo di classe o una struttura di campo privato che non fornisce alcun mezzo di mutazione oltre alla sostituzione. Se, tuttavia, un tipo dovrebbe comportarsi come un mucchio di variabili correlate ma indipendenti attaccate al nastro adesivo, allora si dovrebbe usare un tipo che è un gruppo di variabili incollate insieme al nastro adesivo - una struttura a campo scoperto. Matrici di questo tipo sono molto efficienti con cui lavorare e hanno una semantica molto pulita. L'uso di qualsiasi altro tipo porterà a semantiche confuse, prestazioni inferiori o entrambe.

    
risposta data 10.12.2014 - 21:58
fonte
0

Una differenza importante è l'allocazione della memoria. Ad esempio, attraversare una lista collegata può comportare molti errori di cache e prestazioni più lente, mentre una matrice rappresenta un blocco contiguo di memoria che contiene più istanze di un particolare tipo di dati, e lo attraversa in ordine è più probabile che colpisca la CPU cache.

Naturalmente, una serie di riferimenti a oggetti potrebbe non beneficiare tanto degli hit della cache, poiché il dereferenziamento potrebbe comunque portarti ovunque nella memoria.

Poi ci sono implementazioni di liste come ArrayList, che implementano un elenco usando un array. Sono utili primitive da avere.

    
risposta data 11.12.2014 - 00:07
fonte
-2

Ecco alcune indicazioni che puoi utilizzare quando selezionare Array e quando selezionare List .

  • Utilizza Array quando si torna da un metodo.
  • Utilizza List come variabile quando si costruisce il valore restituito (all'interno del metodo). Quindi usa .ToArray() al ritorno dal metodo.

In generale, usa Array quando non intendi che il consumatore aggiunga oggetti alla raccolta. Utilizza List quando intendi che il consumatore aggiunga elementi alla raccolta.

Array è pensato per gestire collezioni "statiche" mentre List è pensato per gestire raccolte "dinamiche".

    
risposta data 11.12.2014 - 09:06
fonte

Leggi altre domande sui tag