2009-07-18 18 views
21

Sto usando .Net 3.5 (C#) e ho sentito che la performance di C# List<T>.ToArray è "cattiva", poiché la memoria copia tutti gli elementi per formare una nuova matrice. È vero?C# List <T>. Le prestazioni dell'Array sono errate?

+0

Si potrebbe desiderare di vedere [è-da-meglio-to-call-ToList-o-ToArray-in-linq- query] (http://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in-linq-queries) – nawfal

risposta

43

No che non è vero. Le prestazioni sono buone poiché tutto ciò che fa è la memoria copia tutti gli elementi (*) per formare un nuovo array.

Ovviamente dipende da ciò che definite prestazioni "buone" o "cattive".

(*) riferimenti per tipi di riferimento, valori per tipi di valore.

EDIT

In risposta al tuo commento, utilizzando Reflector è un buon modo per verificare l'attuazione (vedi sotto). Oppure pensa solo per un paio di minuti a come lo implementeresti e prendi fiducia che gli ingegneri di Microsoft non escano con una soluzione peggiore.

public T[] ToArray() 
{ 
    T[] destinationArray = new T[this._size]; 
    Array.Copy(this._items, 0, destinationArray, 0, this._size); 
    return destinationArray; 
} 

Naturalmente, le prestazioni "buone" o "cattive" hanno solo un significato relativo ad alcune alternative. Se nel tuo caso specifico, esiste una tecnica alternativa per raggiungere il tuo obiettivo che è misurabilmente più veloce, allora puoi considerare la performance come "cattiva". Se non esiste tale alternativa, le prestazioni sono "buone" (o "abbastanza buone").

EDIT 2

In risposta al commento: "No ri-costruzione di oggetti?":...

n ricostruzione per i tipi di riferimento per tipi di valori vengono copiati i valori, che possono liberamente essere descritto come ricostruzione

+2

Grazie Joe, la tua risposta è così bella!Avete dei documenti correlati per discutere ulteriormente o provare ulteriormente il reclamo - "tutto ciò che fa è la memoria copia tutti gli elementi (*) per formare un nuovo array."? – George2

+0

Grazie a Joe, Array.Copy copia solo il riferimento? Nessuna ri-costruzione di oggetti? – George2

+1

George. Vai a cercare! Oppure vai a usare Reflector e scoprilo. Non era così complesso per ToArray, vero? –

1

crea nuovi riferimenti in un array, ma questo è solo l'unica cosa che questo metodo potrebbe e dovrebbe fare ...

+0

Vuoi dire che solo il riferimento viene copiato? – George2

19

motivi per chiamare ToArray()

  • Se il valore restituito non è pensato per essere modificato, restituirlo come una matrice rende questo fatto un po 'più chiaro.
  • Se si prevede che il chiamante esegua molti accessi non sequenziali ai dati, ci può essere un vantaggio in termini di prestazioni per un array su un elenco <>.
  • Se si sa che sarà necessario passare il valore restituito a una funzione di terze parti che si aspetta un array.
  • Compatibilità con le funzioni di chiamata che devono funzionare con .NET versione 1 o 1.1. Queste versioni non hanno il tipo List <> (o qualsiasi tipo generico, per quello).

Motivi di non chiamare ToArray()

  • Se il chiamante mai ha bisogno di aggiungere o rimuovere elementi, un elenco <> è assolutamente necessaria.
  • I vantaggi prestazionali non sono necessariamente garantiti, soprattutto se il chiamante accede ai dati in modo sequenziale. C'è anche il passaggio aggiuntivo per la conversione da Lista <> alla matrice, che richiede tempo di elaborazione.
  • Il chiamante può sempre convertire l'elenco in una matrice.

tratto da here

+2

Un buon riferimento, ma non una risposta diretta alla mia domanda? Qual è la tua risposta alla mia domanda? – George2

+4

È l'unica risposta che possiamo dare: la correttezza vince sempre sulle prestazioni. Non è la cosa più performante che puoi ancora corretta. L'applicazione di questo è che non si chiama .ToArray() a meno che non si debba comunque. –

+4

"... può esserci un vantaggio in termini di prestazioni per un array su un elenco <>." - nessuna prova per questo? Sembra un mito per me. – Joe

1

prestazioni deve essere intesa in termini relativi Conversione di un array a una lista comporta la copia del array, e il costo dipende dalla dimensione dell'array, ma è necessario confrontare il costo con altre cose che il programma sta facendo. Come hai ottenuto le informazioni da inserire nell'array in primo luogo? era leggendo dal disco, o una connessione di rete, o un database, quindi una copia di matrice in memoria è molto improbabile per fare una differenza rilevabile per il tempo impiegato

+0

"inserire nell'array in primo luogo" significa? – George2

+1

Prima di copiare l'array, è necessario aver ottenuto alcune informazioni da archiviare nell'array, altrimenti non ci sarebbe motivo di crearne una copia. –

4

Sì , è vero che fa una copia di memoria di tutti gli elementi. È un problema di prestazioni? Dipende dai tuoi requisiti di prestazione.

A List contiene un array internamente per contenere tutti gli elementi. L'array cresce se la capacità non è più sufficiente per l'elenco. Ogni volta che succede, la lista copierà tutti gli elementi in una nuova matrice. Succede sempre e per la maggior parte delle persone non ci sono problemi di prestazioni.

E.g. una lista con un costruttore predefinito inizia alla capacità 16, e quando si usa l'elemento 17, .Add(), si crea una nuova matrice di dimensione 32, copia i 16 valori precedenti e aggiunge la diciassettesima.

La differenza di dimensione è anche il motivo per cui ToArray() restituisce una nuova istanza di matrice invece di passare il riferimento privato.

+0

Grazie chris166, voglio solo confermare che solo il riferimento è stato copiato durante ToArray. Nessuna ri-costruzione di oggetti durante ToArray? – George2

+1

Sì, vengono copiati solo i riferimenti. L'elenco non sa come creare una copia profonda dei tuoi oggetti. L'eccezione sono i tipi di valore (structs, ints, double, enum etc). – chris166

0

Per qualsiasi tipo di elenco/ICollection in cui conosce la lunghezza, può allocare una matrice della dimensione esatta fin dall'inizio.

T[] destinationArray = new T[this._size]; 
Array.Copy(this._items, 0, destinationArray, 0, this._size); 
return destinationArray; 

Se il tipo di origine è IEnumerable (non una lista/Collection), allora la fonte è:

items = new TElement[4]; 
.. 
if (no more space) { 
    TElement[] newItems = new TElement[checked(count * 2)]; 
    Array.Copy(items, 0, newItems, 0, count); 
    items = newItems; 

Si inizia alle dimensioni 4 e cresce in modo esponenziale, raddoppiando ogni volta che si esaurisce lo spazio. Ogni volta che raddoppia, deve riallocare la memoria e copiare i dati.

Se conosciamo la dimensione dei dati di origine, possiamo evitare questo leggero sovraccarico. Tuttavia, nella maggior parte dei casi, ad esempio la dimensione dell'array < = 1024, verrà eseguita così rapidamente che non è nemmeno necessario pensare a questi dettagli di implementazione.

Riferimenti: Enumerable.cs, List.cs (F12ing in loro), la risposta di Joe

Problemi correlati