2009-07-18 10 views
21

Estoy usando .Net 3.5 (C#) y he escuchado que el rendimiento de C# List<T>.ToArray es "malo", ya que las copias de memoria de todos los elementos forman una nueva matriz. ¿Es eso cierto?C# List <T>. ¿El rendimiento de ToArray es malo?

+0

Es posible que desee ver [es-lo-mejor-para-llamar-tolist-o-toArray-en-linq- consultas] (http://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in-linq-queries) – nawfal

Respuesta

43

Sin eso no es verdad. El rendimiento es bueno ya que todo lo que hace es copiar todos los elementos de la memoria (*) para formar una nueva matriz.

Por supuesto, depende de lo que defina como un rendimiento "bueno" o "malo".

(*) referencias para tipos de referencia, valores para tipos de valores.

EDITAR

En respuesta a tu comentario, usando reflector es una buena manera de comprobar la aplicación (ver más abajo). O simplemente piense por un par de minutos cómo lo implementaría, y confíe en que los ingenieros de Microsoft no encontrarán una solución peor.

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

Por supuesto, el rendimiento "bueno" o "malo" solo tiene un significado relativo a alguna alternativa. Si en su caso específico, existe una técnica alternativa para lograr su meta que es considerablemente más rápida, entonces puede considerar que el rendimiento es "malo". Si no existe tal alternativa, entonces el rendimiento es "bueno" (o "lo suficientemente bueno").

EDIT 2

En respuesta al comentario: "No se re-construcción de objetos?":...

No reconstrucción para los tipos de referencia para los tipos de valor se copian los valores, que libremente podría ser descrito como la reconstrucción

+2

Gracias Joe, ¡tu respuesta es genial!¿Tiene algún documento relacionado para analizarlo o probar más a fondo el reclamo? "Todo lo que hace es copiar todos los elementos de memoria (*) para formar un nuevo conjunto". – George2

+0

Gracias a Joe, Array.Copy solo copia la referencia? No hay reconstrucción de objetos? – George2

+1

George. Ve a buscarlo! O ve a usar Reflector y averígualo. No fue tan complejo para ToArray, ¿verdad? –

1

crea nuevas referencias en una matriz, pero eso es sólo la única cosa que este método podría y debería hacer ...

+0

¿Quiere decir que solo se copia la referencia? – George2

19

razones para llamar ToArray()

  • Si el valor devuelto no es pretende ser modificado, devolverlo como una matriz hace que ese hecho sea un poco más claro.
  • Si se espera que el que llama realice muchos accesos no secuenciales a los datos, puede haber un beneficio de rendimiento para una matriz sobre una lista <>.
  • Si sabe, tendrá que pasar el valor devuelto a una función de un tercero que espera una matriz.
  • Compatibilidad con funciones de llamada que necesitan funcionar con .NET versión 1 o 1.1. Estas versiones no tienen el tipo List <> (ni ningún tipo genérico, para el caso).

Razones para no llamar ToArray()

  • Si la persona que llama siempre tiene necesidad de añadir o eliminar elementos, una lista <> es absolutamente necesario.
  • Los beneficios de rendimiento no están necesariamente garantizados, especialmente si la persona que llama está accediendo a los datos de forma secuencial. También está el paso adicional de convertir de List <> a array, lo que requiere tiempo de procesamiento.
  • La persona que llama siempre puede convertir la lista en una matriz.

tomado de here

+2

Buena referencia, pero no respuesta directa a mi pregunta? ¿Cuál es tu respuesta a mi pregunta? – George2

+4

Es la única respuesta que podemos dar: la corrección siempre triunfa sobre el rendimiento. No haces lo más eficiente que puedes, eso sigue siendo correcto. La aplicación de eso es que no llamas a .ToArray() a menos que de todos modos tengas que hacerlo. –

+4

"... puede haber un beneficio de rendimiento para una matriz sobre una Lista <>." - Alguna evidencia para esto? Me parece un mito. – Joe

1

rendimiento tiene que ser entendida en términos relativos en la conversión de una matriz a una Lista implica copiar el array, y el costo de eso dependerá del tamaño de la matriz. Pero tiene que comparar ese costo con otras cosas que está haciendo su programa. ¿Cómo obtuvo la información para poner en la matriz en primer lugar? fue leyendo desde el disco, o una conexión de red, o una base de datos, entonces una copia de matriz en la memoria es muy poco probable que haga una diferencia detectable en el tiempo tomado.

+0

"poner en la matriz en primer lugar" significa? – George2

+1

Antes de copiar la matriz, debe haber obtenido cierta información para almacenar en la matriz, de lo contrario no habría ninguna razón para hacer una copia de la misma. –

4

Sí , es verdad que hace una copia de memoria de todos los elementos. ¿Es un problema de rendimiento? Eso depende de sus requisitos de rendimiento.

A List contiene una matriz internamente para contener todos los elementos. La matriz crece si la capacidad ya no es suficiente para la lista. En cualquier momento que eso suceda, la lista copiará todos los elementos en una nueva matriz. Eso sucede todo el tiempo, y para la mayoría de la gente eso no es un problema de rendimiento.

E.g. una lista con un constructor predeterminado comienza en la capacidad 16, y cuando .Add() el 17mo elemento, crea una nueva matriz de tamaño 32, copia los 16 valores antiguos y agrega la 17ma.

La diferencia de tamaño también es la razón por la que ToArray() devuelve una nueva instancia de matriz en lugar de pasar la referencia privada.

+0

Gracias chris166, solo quiero confirmar que solo se copia la referencia durante ToArray. No hay reconstrucción de objetos durante ToArray? – George2

+1

Sí, solo se copian las referencias. La lista no sabe cómo crear una copia profunda de sus objetos. La excepción son los tipos de valores (structs, ints, doubles, enums, etc.). – chris166

0

Para cualquier tipo de List/ICollection donde conoce la longitud, puede asignar una matriz de exactamente el tamaño correcto desde el principio.

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

Si el tipo de fuente es IEnumerable (no es una lista/Colección), entonces la fuente es:

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

Se inicia en tamaño 4 y crece exponencialmente, duplicándose cada vez que se queda sin espacio. Cada vez que se duplica, tiene que reasignar la memoria y copiar los datos.

Si conocemos el tamaño de la fuente de datos, podemos evitar esta ligera sobrecarga. Sin embargo, en la mayoría de los casos, por ejemplo, tamaño de matriz < = 1024, se ejecutará tan rápido que ni siquiera necesitamos pensar en este detalle de implementación.

Referencias: Enumerable.cs, List.cs (F12ing en ellos), la respuesta de Joe

Cuestiones relacionadas