Está claro que el rendimiento de búsqueda de la HashSet<T>
clase genérica es mayor que el de la List<T>
clase genérica . Simplemente compare la clave basada en hash con el enfoque lineal en la List<T>
clase.
Sin embargo, calcular una clave hash puede tomar algunos ciclos de CPU, por lo que para una pequeña cantidad de elementos, la búsqueda lineal puede ser una alternativa real a la HashSet<T>
.
Mi pregunta: ¿dónde está el punto de equilibrio?
Para simplificar el escenario (y para ser justos) supongamos que la List<T>
clase usa el Equals()
método del elemento para identificar un elemento.
.net
performance
collections
list
hash
Michael Damatov
fuente
fuente
Respuestas:
Mucha gente dice que una vez que llegue al tamaño en que la velocidad es realmente una preocupación que
HashSet<T>
siempre superaráList<T>
, pero eso depende de lo que esté haciendo.Digamos que tienes un
List<T>
que solo tendrá un promedio de 5 elementos. Durante un gran número de ciclos, si se agrega o elimina un solo elemento en cada ciclo, es mejor que utilice aList<T>
.Hice una prueba para esto en mi máquina y, bueno, tiene que ser muy muy pequeño para obtener una ventaja
List<T>
. Para una lista de cadenas cortas, la ventaja se fue después del tamaño 5, para los objetos después del tamaño 20.Aquí están los datos que se muestran como un gráfico:
Aquí está el código:
fuente
List<T>
motor de juego, y dado que generalmente tendré un gran volumen de objetos, este tipo de colección sería perfecto.Estás mirando esto mal. Sí, una búsqueda lineal de una Lista superará a un HashSet para una pequeña cantidad de elementos. Pero la diferencia de rendimiento generalmente no importa para colecciones tan pequeñas. En general, es de las grandes colecciones de las que tiene que preocuparse, y ahí es donde piensa en términos de Big-O . Sin embargo, si ha medido un cuello de botella real en el rendimiento de HashSet, puede intentar crear una Lista / HashSet híbrido, pero lo hará realizando muchas pruebas de rendimiento empíricas, sin hacer preguntas sobre SO.
fuente
when small collection becomes large enough to worry about HashSet vs List?
decenas, decenas de miles, miles de millones de elementos?HashSet<T>
. En los casos de números pequeños dondeList<T>
podría ser más rápido, la diferencia es insignificante ".Es esencialmente inútil comparar dos estructuras de rendimiento que se comportan de manera diferente. Use la estructura que transmite la intención. Incluso si dice
List<T>
que no tendría duplicados y el orden de iteración no importa hacerlo comparable a unHashSet<T>
, sigue siendo una mala elecciónList<T>
porque es relativamente menos tolerante a fallas.Dicho esto, inspeccionaré algunos otros aspectos del rendimiento,
Aunque la adición es O (1) en ambos casos, será relativamente más lento en HashSet ya que implica el costo de precalcular el código hash antes de almacenarlo.
La escalabilidad superior de HashSet tiene un costo de memoria. Cada entrada se almacena como un nuevo objeto junto con su código hash. Este artículo puede darte una idea.
fuente
Si usar un HashSet <> o List <> se reduce a cómo necesita acceder a su colección . Si necesita garantizar el orden de los artículos, use una Lista. Si no lo hace, use un HashSet. Deje que Microsoft se preocupe por la implementación de sus algoritmos y objetos de hashing.
Un HashSet accederá a los elementos sin tener que enumerar la colección (complejidad de O (1) o cerca de ella), y debido a que una Lista garantiza el orden, a diferencia de un HashSet, algunos elementos tendrán que enumerarse (complejidad de O (n)).
fuente
List
se prefiere a, porque puede recordar un índice, esa es la situación están describiendoSolo pensé en intervenir con algunos puntos de referencia para diferentes escenarios para ilustrar las respuestas anteriores:
Y para cada escenario, busque valores que aparecen:
Antes de cada escenario, generaba listas de cadenas aleatorias de tamaño aleatorio, y luego alimentaba cada lista a un hashset. Cada escenario se ejecutó 10,000 veces, esencialmente:
(seudocódigo de prueba)
Salida de muestra
Probado en Windows 7, 12GB Ram, 64 bit, Xeon 2.8GHz
fuente
List
todavía toma solo 0,17 milisegundos para realizar una sola búsqueda, y no es probable que requiera una sustituciónHashSet
hasta que la frecuencia de búsqueda alcance niveles absurdos. Para entonces, el uso de List generalmente es el menor de los problemas.El punto de equilibrio dependerá del costo de calcular el hash. Los cálculos de hash pueden ser triviales o no ... :-) Siempre existe la clase System.Collections.Specialized.HybridDictionary para ayudarlo a no tener que preocuparse por el punto de equilibrio.
fuente
La respuesta, como siempre, es " depende ". Asumo por las etiquetas de las que estás hablando C #.
Su mejor apuesta es determinar
y escribir algunos casos de prueba.
También depende de cómo ordena la lista (si está ordenada), qué tipo de comparaciones deben realizarse, cuánto tiempo lleva la operación "Comparar" para el objeto en particular en la lista, o incluso cómo piensa usar colección.
En general, el mejor para elegir no se basa tanto en el tamaño de los datos con los que está trabajando, sino más bien en cómo piensa acceder a ellos. ¿Tiene cada pieza de datos asociada con una cadena particular u otros datos? Una colección basada en hash probablemente sería lo mejor. ¿Es importante el orden de los datos que está almacenando o va a necesitar acceder a todos los datos al mismo tiempo? Una lista regular puede ser mejor entonces.
Adicional:
Por supuesto, mis comentarios anteriores suponen que "rendimiento" significa acceso a datos. Algo más a tener en cuenta: ¿qué buscas cuando dices "rendimiento"? ¿Se busca el valor individual del rendimiento? ¿Es gestión de grandes conjuntos de valores (10000, 100000 o más)? ¿Es el rendimiento de llenar la estructura de datos con datos? ¿Eliminar datos? ¿Acceso a bits de datos individuales? ¿Reemplazar valores? Iterando sobre los valores? ¿Uso de memoria? Velocidad de copia de datos? Por ejemplo, si accede a los datos por un valor de cadena, pero su principal requisito de rendimiento es un uso mínimo de memoria, es posible que tenga problemas de diseño en conflicto.
fuente
Puede usar un HybridDictionary que detecta automáticamente el punto de ruptura y acepta valores nulos, lo que lo hace esencialmente igual que un HashSet.
fuente
Depende. Si la respuesta exacta realmente importa, haga un perfil y averígüelo. Si está seguro de que nunca tendrá más de un cierto número de elementos en el conjunto, vaya con una Lista. Si el número no tiene límites, use un HashSet.
fuente
Depende de lo que estés haciendo. Si sus claves son enteras, probablemente no necesite muchos elementos antes de que el HashSet sea más rápido. Si lo está escribiendo en una cadena, será más lento y dependerá de la cadena de entrada.
¿Seguramente podrías preparar un punto de referencia con bastante facilidad?
fuente
Un factor que no tiene en cuenta es la solidez de la función GetHashcode (). Con una función hash perfecta, el HashSet claramente tendrá un mejor rendimiento de búsqueda. Pero a medida que disminuye la función hash, también lo hará el tiempo de búsqueda de HashSet.
fuente
Depende de muchos factores ... Implementación de la lista, arquitectura de CPU, JVM, semántica de bucle, complejidad del método igual, etc. Para cuando la lista se vuelva lo suficientemente grande como para comparar de manera efectiva (más de 1000 elementos), binario basado en hash las búsquedas superan las búsquedas lineales, y la diferencia solo aumenta a partir de ahí.
¡Espero que esto ayude!
fuente