Rendimiento de HashSet vs. List

406

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.

Michael Damatov
fuente
77
Si realmente desea minimizar el tiempo de búsqueda, considere también las matrices y las matrices ordenadas. Para responder correctamente a esta pregunta, se necesita un punto de referencia, pero debe contarnos más sobre T. Además, el rendimiento de HashSet puede verse afectado por el tiempo de ejecución de T.GetHashCode ().
Eldritch Conundrum

Respuestas:

821

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 a List<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.

1 item LIST strs time: 617ms
1 item HASHSET strs time: 1332ms

2 item LIST strs time: 781ms
2 item HASHSET strs time: 1354ms

3 item LIST strs time: 950ms
3 item HASHSET strs time: 1405ms

4 item LIST strs time: 1126ms
4 item HASHSET strs time: 1441ms

5 item LIST strs time: 1370ms
5 item HASHSET strs time: 1452ms

6 item LIST strs time: 1481ms
6 item HASHSET strs time: 1418ms

7 item LIST strs time: 1581ms
7 item HASHSET strs time: 1464ms

8 item LIST strs time: 1726ms
8 item HASHSET strs time: 1398ms

9 item LIST strs time: 1901ms
9 item HASHSET strs time: 1433ms

1 item LIST objs time: 614ms
1 item HASHSET objs time: 1993ms

4 item LIST objs time: 837ms
4 item HASHSET objs time: 1914ms

7 item LIST objs time: 1070ms
7 item HASHSET objs time: 1900ms

10 item LIST objs time: 1267ms
10 item HASHSET objs time: 1904ms

13 item LIST objs time: 1494ms
13 item HASHSET objs time: 1893ms

16 item LIST objs time: 1695ms
16 item HASHSET objs time: 1879ms

19 item LIST objs time: 1902ms
19 item HASHSET objs time: 1950ms

22 item LIST objs time: 2136ms
22 item HASHSET objs time: 1893ms

25 item LIST objs time: 2357ms
25 item HASHSET objs time: 1826ms

28 item LIST objs time: 2555ms
28 item HASHSET objs time: 1865ms

31 item LIST objs time: 2755ms
31 item HASHSET objs time: 1963ms

34 item LIST objs time: 3025ms
34 item HASHSET objs time: 1874ms

37 item LIST objs time: 3195ms
37 item HASHSET objs time: 1958ms

40 item LIST objs time: 3401ms
40 item HASHSET objs time: 1855ms

43 item LIST objs time: 3618ms
43 item HASHSET objs time: 1869ms

46 item LIST objs time: 3883ms
46 item HASHSET objs time: 2046ms

49 item LIST objs time: 4218ms
49 item HASHSET objs time: 1873ms

Aquí están los datos que se muestran como un gráfico:

ingrese la descripción de la imagen aquí

Aquí está el código:

static void Main(string[] args)
{
    int times = 10000000;


    for (int listSize = 1; listSize < 10; listSize++)
    {
        List<string> list = new List<string>();
        HashSet<string> hashset = new HashSet<string>();

        for (int i = 0; i < listSize; i++)
        {
            list.Add("string" + i.ToString());
            hashset.Add("string" + i.ToString());
        }

        Stopwatch timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < times; i++)
        {
            list.Remove("string0");
            list.Add("string0");
        }
        timer.Stop();
        Console.WriteLine(listSize.ToString() + " item LIST strs time: " + timer.ElapsedMilliseconds.ToString() + "ms");


        timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < times; i++)
        {
            hashset.Remove("string0");
            hashset.Add("string0");
        }
        timer.Stop();
        Console.WriteLine(listSize.ToString() + " item HASHSET strs time: " + timer.ElapsedMilliseconds.ToString() + "ms");
        Console.WriteLine();
    }


    for (int listSize = 1; listSize < 50; listSize+=3)
    {
        List<object> list = new List<object>();
        HashSet<object> hashset = new HashSet<object>();

        for (int i = 0; i < listSize; i++)
        {
            list.Add(new object());
            hashset.Add(new object());
        }

        object objToAddRem = list[0];

        Stopwatch timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < times; i++)
        {
            list.Remove(objToAddRem);
            list.Add(objToAddRem);
        }
        timer.Stop();
        Console.WriteLine(listSize.ToString() + " item LIST objs time: " + timer.ElapsedMilliseconds.ToString() + "ms");



        timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < times; i++)
        {
            hashset.Remove(objToAddRem);
            hashset.Add(objToAddRem);
        }
        timer.Stop();
        Console.WriteLine(listSize.ToString() + " item HASHSET objs time: " + timer.ElapsedMilliseconds.ToString() + "ms");
        Console.WriteLine();
    }

    Console.ReadLine();
}
innominado227
fuente
8
Muchas gracias! Esta es una gran explicación, estaba buscando algo que pudiera agregar y eliminar más rápido que un List<T>motor de juego, y dado que generalmente tendré un gran volumen de objetos, este tipo de colección sería perfecto.
redcodefinal
17
En realidad, hay una colección en .NET Framework que cambia entre una implementación de lista y hastableble dependiendo de la cantidad de elementos que contiene: HybridDictionary .
MgSam 01 de
8
MS parece haberlo abandonado, ya que solo tiene disponible una versión no genérica.
MgSam el
47
Tan completa como es esta respuesta, no puede responder la pregunta original sobre el rendimiento de la búsqueda de lista vs hashset. Está probando qué tan rápido puede insertarlos y eliminarlos, lo que requiere mucho más tiempo y características de rendimiento diferentes que la búsqueda. Inténtalo de nuevo, usando .Contains, y tu gráfico cambiará significativamente.
Robert McKee
55
@hypehuman, la CPU no puede trabajar directamente en los datos de la memoria del sistema, pero extrae los datos de la memoria a su caché para trabajar. Hay un retraso significativo entre la solicitud de mover la memoria y la memoria que realmente llega, por lo que la CPU a menudo solicita que se mueva una gran cantidad de memoria contigua a la vez. La idea detrás de esto es que la memoria necesaria para la siguiente instrucción probablemente esté muy cerca de la memoria utilizada por la instrucción anterior y, por lo tanto, a menudo ya está en el caché. Cuando sus datos se encuentran dispersos por toda la memoria, se reduce la posibilidad de tener suerte.
Roy T.
70

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.

Eloff
fuente
55
grandes colecciones de las que tienes que preocuparte . ¿Podemos redefinir esa pregunta en términos de when small collection becomes large enough to worry about HashSet vs List?decenas, decenas de miles, miles de millones de elementos?
om-nom-nom
8
No, verá una considerable diferencia de rendimiento por encima de unos pocos cientos de elementos. El punto es siempre usar un HashSet si está haciendo los tipos de accesos en los que HashSet es bueno (por ejemplo, es el elemento X en el conjunto). Si su colección es tan pequeña que una Lista es más rápida, entonces es muy raro que esas búsquedas son realmente un cuello de botella en su aplicación. Si puede medirlo como uno, está bien, puede intentar optimizarlo, pero de lo contrario está perdiendo el tiempo.
Eloff
15
¿Qué pasa si tiene una pequeña colección que se ve afectada muchas veces en un bucle? Ese no es un escenario poco común.
dan-gph
3
@ om-nom-nom - Creo que el punto es que no importa dónde esté el punto de inflexión, porque: "Si el rendimiento es una preocupación, úselo HashSet<T>. En los casos de números pequeños donde List<T>podría ser más rápido, la diferencia es insignificante ".
Scott Smith
66

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 un HashSet<T>, sigue siendo una mala elección List<T>porque es relativamente menos tolerante a fallas.

Dicho esto, inspeccionaré algunos otros aspectos del rendimiento,

+------------+--------+-------------+-----------+----------+----------+-----------+
| Collection | Random | Containment | Insertion | Addition |  Removal | Memory    |
|            | access |             |           |          |          |           |
+------------+--------+-------------+-----------+----------+----------+-----------+
| List<T>    | O(1)   | O(n)        | O(n)      | O(1)*    | O(n)     | Lesser    |
| HashSet<T> | O(n)   | O(1)        | n/a       | O(1)     | O(1)     | Greater** |
+------------+--------+-------------+-----------+----------+----------+-----------+
  • 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.

nawfal
fuente
11
Mi pregunta (hace seis años) no era sobre el desempeño teórico .
Michael Damatov
1
HashSet permite el acceso aleatorio con ElementAt (), y creo que ese sería el momento O (n). Además, quizás podría poner en su tabla si cada colección permite duplicados (por ejemplo: las listas sí, pero los hashsets no).
Dan W
1
@DanW en la tabla estoy comparando puramente rendimiento, no características de comportamiento. Gracias por el consejo de ElementAt.
nawfal
1
ElementAt es solo una extensión de LINQ ... no hace nada que no pueda hacer y optimiza mejor en otro método que agregue usted mismo. Creo que la tabla tenía más sentido sin considerar ElementAt ya que todos los demás métodos existen explícitamente en esas clases.
Dinerdo el
1
Gracias por esta tabla, en mi caso de uso necesito agregar y eliminar destinos a una colección poblada cada vez que se habilitan / deshabilitan y esto me ayudó a tomar la decisión correcta (HashSet).
Casey Hofland
50

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)).

núcleo
fuente
La lista podría calcular el desplazamiento para el elemento específico por su índice (porque todos los elementos son del mismo tipo y potencialmente ocupan el mismo tamaño de memoria). Entonces la Lista no es necesaria enumera sus elementos
Lu55
@ Lu55: la pregunta es sobre la búsqueda de un elemento en una colección. Un escenario típico es que la colección es dinámica : es posible que se hayan agregado o eliminado elementos desde la última vez que buscó un elemento determinado, por lo que un índice no es significativo (porque habrá cambiado). Si tiene una colección estática (que no cambiará mientras realiza sus cálculos), o los elementos nunca se eliminan, y siempre se agregan al final, entonces Listse prefiere a, porque puede recordar un índice, esa es la situación están describiendo
ToolmakerSteve
Puede usar un SortedSet si necesita ordenar un HashSet. Todavía mucho más rápido que una Lista.
live-love
25

Solo pensé en intervenir con algunos puntos de referencia para diferentes escenarios para ilustrar las respuestas anteriores:

  1. Algunas (12-20) cadenas pequeñas (longitud entre 5 y 10 caracteres)
  2. Muchas (~ 10K) cuerdas pequeñas
  3. Algunas cadenas largas (longitud entre 200 y 1000 caracteres)
  4. Muchas (~ 5K) cuerdas largas
  5. Unos cuantos enteros
  6. Muchos enteros (~ 10K)

Y para cada escenario, busque valores que aparecen:

  1. Al principio de la lista ("inicio", índice 0)
  2. Cerca del comienzo de la lista ("temprano", índice 1)
  3. En el medio de la lista ("medio", índice de conteo / 2)
  4. Cerca del final de la lista ("tarde", índice cuenta-2)
  5. Al final de la lista ("fin", índice cuenta-1)

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)

stopwatch.start
for X times
    exists = list.Contains(lookup);
stopwatch.stop

stopwatch.start
for X times
    exists = hashset.Contains(lookup);
stopwatch.stop

Salida de muestra

Probado en Windows 7, 12GB Ram, 64 bit, Xeon 2.8GHz

---------- Testing few small strings ------------
Sample items: (16 total)
vgnwaloqf diwfpxbv tdcdc grfch icsjwk
...

Benchmarks:
1: hashset: late -- 100.00 % -- [Elapsed: 0.0018398 sec]
2: hashset: middle -- 104.19 % -- [Elapsed: 0.0019169 sec]
3: hashset: end -- 108.21 % -- [Elapsed: 0.0019908 sec]
4: list: early -- 144.62 % -- [Elapsed: 0.0026607 sec]
5: hashset: start -- 174.32 % -- [Elapsed: 0.0032071 sec]
6: list: middle -- 187.72 % -- [Elapsed: 0.0034536 sec]
7: list: late -- 192.66 % -- [Elapsed: 0.0035446 sec]
8: list: end -- 215.42 % -- [Elapsed: 0.0039633 sec]
9: hashset: early -- 217.95 % -- [Elapsed: 0.0040098 sec]
10: list: start -- 576.55 % -- [Elapsed: 0.0106073 sec]


---------- Testing many small strings ------------
Sample items: (10346 total)
dmnowa yshtrxorj vthjk okrxegip vwpoltck
...

Benchmarks:
1: hashset: end -- 100.00 % -- [Elapsed: 0.0017443 sec]
2: hashset: late -- 102.91 % -- [Elapsed: 0.0017951 sec]
3: hashset: middle -- 106.23 % -- [Elapsed: 0.0018529 sec]
4: list: early -- 107.49 % -- [Elapsed: 0.0018749 sec]
5: list: start -- 126.23 % -- [Elapsed: 0.0022018 sec]
6: hashset: early -- 134.11 % -- [Elapsed: 0.0023393 sec]
7: hashset: start -- 372.09 % -- [Elapsed: 0.0064903 sec]
8: list: middle -- 48,593.79 % -- [Elapsed: 0.8476214 sec]
9: list: end -- 99,020.73 % -- [Elapsed: 1.7272186 sec]
10: list: late -- 99,089.36 % -- [Elapsed: 1.7284155 sec]


---------- Testing few long strings ------------
Sample items: (19 total)
hidfymjyjtffcjmlcaoivbylakmqgoiowbgxpyhnrreodxyleehkhsofjqenyrrtlphbcnvdrbqdvji...
...

Benchmarks:
1: list: early -- 100.00 % -- [Elapsed: 0.0018266 sec]
2: list: start -- 115.76 % -- [Elapsed: 0.0021144 sec]
3: list: middle -- 143.44 % -- [Elapsed: 0.0026201 sec]
4: list: late -- 190.05 % -- [Elapsed: 0.0034715 sec]
5: list: end -- 193.78 % -- [Elapsed: 0.0035395 sec]
6: hashset: early -- 215.00 % -- [Elapsed: 0.0039271 sec]
7: hashset: end -- 248.47 % -- [Elapsed: 0.0045386 sec]
8: hashset: start -- 298.04 % -- [Elapsed: 0.005444 sec]
9: hashset: middle -- 325.63 % -- [Elapsed: 0.005948 sec]
10: hashset: late -- 431.62 % -- [Elapsed: 0.0078839 sec]


---------- Testing many long strings ------------
Sample items: (5000 total)
yrpjccgxjbketcpmnvyqvghhlnjblhgimybdygumtijtrwaromwrajlsjhxoselbucqualmhbmwnvnpnm
...

Benchmarks:
1: list: early -- 100.00 % -- [Elapsed: 0.0016211 sec]
2: list: start -- 132.73 % -- [Elapsed: 0.0021517 sec]
3: hashset: start -- 231.26 % -- [Elapsed: 0.003749 sec]
4: hashset: end -- 368.74 % -- [Elapsed: 0.0059776 sec]
5: hashset: middle -- 385.50 % -- [Elapsed: 0.0062493 sec]
6: hashset: late -- 406.23 % -- [Elapsed: 0.0065854 sec]
7: hashset: early -- 421.34 % -- [Elapsed: 0.0068304 sec]
8: list: middle -- 18,619.12 % -- [Elapsed: 0.3018345 sec]
9: list: end -- 40,942.82 % -- [Elapsed: 0.663724 sec]
10: list: late -- 41,188.19 % -- [Elapsed: 0.6677017 sec]


---------- Testing few ints ------------
Sample items: (16 total)
7266092 60668895 159021363 216428460 28007724
...

Benchmarks:
1: hashset: early -- 100.00 % -- [Elapsed: 0.0016211 sec]
2: hashset: end -- 100.45 % -- [Elapsed: 0.0016284 sec]
3: list: early -- 101.83 % -- [Elapsed: 0.0016507 sec]
4: hashset: late -- 108.95 % -- [Elapsed: 0.0017662 sec]
5: hashset: middle -- 112.29 % -- [Elapsed: 0.0018204 sec]
6: hashset: start -- 120.33 % -- [Elapsed: 0.0019506 sec]
7: list: late -- 134.45 % -- [Elapsed: 0.0021795 sec]
8: list: start -- 136.43 % -- [Elapsed: 0.0022117 sec]
9: list: end -- 169.77 % -- [Elapsed: 0.0027522 sec]
10: list: middle -- 237.94 % -- [Elapsed: 0.0038573 sec]


---------- Testing many ints ------------
Sample items: (10357 total)
370826556 569127161 101235820 792075135 270823009
...

Benchmarks:
1: list: early -- 100.00 % -- [Elapsed: 0.0015132 sec]
2: hashset: end -- 101.79 % -- [Elapsed: 0.0015403 sec]
3: hashset: early -- 102.08 % -- [Elapsed: 0.0015446 sec]
4: hashset: middle -- 103.21 % -- [Elapsed: 0.0015618 sec]
5: hashset: late -- 104.26 % -- [Elapsed: 0.0015776 sec]
6: list: start -- 126.78 % -- [Elapsed: 0.0019184 sec]
7: hashset: start -- 130.91 % -- [Elapsed: 0.0019809 sec]
8: list: middle -- 16,497.89 % -- [Elapsed: 0.2496461 sec]
9: list: end -- 32,715.52 % -- [Elapsed: 0.4950512 sec]
10: list: late -- 33,698.87 % -- [Elapsed: 0.5099313 sec]
drzaus
fuente
77
Interesante. Gracias por ejecutar esto. Lamentablemente, sospecho que estas discusiones desencadenan refactorizaciones innecesarias. Afortunadamente, la conclusión para la mayoría de las personas es que, en el peor de los casos, Listtodavía toma solo 0,17 milisegundos para realizar una sola búsqueda, y no es probable que requiera una sustitución HashSethasta que la frecuencia de búsqueda alcance niveles absurdos. Para entonces, el uso de List generalmente es el menor de los problemas.
Paul Walls
Esto no es información real por ahora ... O tal vez está originalmente equivocado ... Acabo de comprobar pequeños valores de 2 a 8 caracteres. List / HashSet se crearon para cada 10 valores ... HashSet más lento para 30% ... Si se usa la capacidad en List, la diferencia es incluso ~ 40%. HashSet se vuelve más rápido en un 10% solo si la Lista no tiene la capacidad especificada y verifica cada valor antes de agregar a través de la lista completa.
Maxim
Si el recuento de artículos se reduce a 4, la Lista vuelve a ganar incluso en el peor escenario (con una diferencia del 10%) Por lo tanto, no recomiendo usar HashSet para una pequeña colección de cadenas (digamos <20). Y es lo que es diferente de sus "pocas pruebas pequeñas".
Maxim
1
@Maxim realmente no puede decir que mis resultados son "incorrectos", es lo que sucedió en mi máquina. YMMV. De hecho, simplemente los volví a ejecutar ( gist.github.com/zaus/014ac9b5a78b267aa1643d63d30c7554 ) en una nueva computadora de estado sólido Win10 4.0GHz 16GB y obtuve resultados similares. La conclusión que veo es que el rendimiento de hashset fue más constante sin importar dónde estaba la clave de búsqueda o cuán grande era la lista, mientras que el rendimiento de la lista varió enormemente de mejor a más de 300 veces más lento. Pero como PaulWalls comentó inicialmente, estamos hablando de #microoptimización seria.
drzaus
@Maxim para referencia: dotnetfiddle.net/5taRDd : siéntase libre de jugar con él.
drzaus
10

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.

Walden Leverich
fuente
1
También debe tener en cuenta el costo de hacer una comparación. En el caso de Contiene (T), el HashSet hará una comparación para verificar que no tiene una colisión de Hash y la Lista hace una comparación de cada elemento que mira antes de encontrar el correcto. También debe tener en cuenta la distribución de los hashes generados por T.GetHashCode () como si esto siempre devuelve el mismo valor que básicamente está haciendo que HashSet haga lo mismo que List.
Martin Brown
6

La respuesta, como siempre, es " depende ". Asumo por las etiquetas de las que estás hablando C #.

Su mejor apuesta es determinar

  1. Un conjunto de datos
  2. Requisitos de uso

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.

Robert P
fuente
5

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.

Muis
fuente
1
Elegí esto para la idea, pero nadie lo usa hoy. Di no a los no genéricos. También un diccionario es una asignación de valores clave, el conjunto no lo es.
nawfal
4

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.

Adam Rosenfield
fuente
3

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?

Peter
fuente
3

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.

JaredPar
fuente
0

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!

Kyle
fuente
1
JVM ... o CLR :-)
bvgheluwe