¿Cuál es la diferencia entre HashSet <T> y List <T>?


156

¿Puedes explicar cuál es la diferencia entre HashSet<T>y List<T>en .NET?

¿Quizás pueda explicar con un ejemplo en qué casos se HashSet<T>debe preferir List<T>?



Le sugiero que consulte los artículos de Wikipedia en en.wikipedia.org/wiki/Hash_table y en.wikipedia.org/wiki/Dynamic_array .
mqp

Para el rendimiento relacionado, vea hashset-vs-list-performance
nawfal

Respuestas:


213

A diferencia de una Lista <> ...

  1. Un HashSet es una Lista sin miembros duplicados.

  2. Debido a que un HashSet está limitado a contener solo entradas únicas, la estructura interna está optimizada para la búsqueda (en comparación con una lista): es considerablemente más rápido

  3. Agregar a un HashSet devuelve un valor booleano: falso si la adición falla debido a que ya existe en Set

  4. Puede realizar operaciones de conjuntos matemáticos contra un Conjunto: Unión / Intersección / IsSubsetOf, etc.

  5. HashSet no implementa IList solo ICollection

  6. No puede usar índices con un HashSet, solo enumeradores.

La razón principal para usar un HashSet sería si está interesado en realizar operaciones Set.

Dados 2 conjuntos: hashSet1 y hashSet2

 //returns a list of distinct items in both sets
 HashSet set3 = set1.Union( set2 );

vuela en comparación con una operación equivalente usando LINQ. ¡También es más ordenado escribir!


IDK, tuve problemas con el Unionmétodo. Había usado en su UnionWithlugar.
usuario

2
+1 para "La razón principal para usar un HashedSet sería si está interesado en realizar operaciones de Set".
LCJ

12
En realidad, prefiero la respuesta que señala que los HashSets son adecuados en los casos en que puede tratar su colección como "artículos de bolsa". Las operaciones de configuración no son tan frecuentes como las comprobaciones de contención. En cualquier momento que tenga un conjunto de elementos únicos (por ejemplo, códigos) y necesite verificar la contención, un HashSet es útil.
ThunderGr

Buena respuesta. Estoy tentado a agregar algunas diferencias características de rendimiento también.
nawfal

1
Pregunta: ¿la razón principal es no tener la certeza de no tener elementos duplicados?
Andrea Scarafoni

54

Para ser más precisos, demostremos con ejemplos,

No puede usar HashSet como en el siguiente ejemplo.

HashSet<string> hashSet1 = new HashSet<string>(){"1","2","3"};
for (int i = 0; i < hashSet1.Count; i++)
    Console.WriteLine(hashSet1[i]);

hashSet1[i] produciría un error:

No se puede aplicar la indexación con [] a una expresión de tipo 'System.Collections.Generic.HashSet'

Puede usar cada declaración:

foreach (var item in hashSet1)
    Console.WriteLine(item);

No puede agregar elementos duplicados a HashSet mientras List le permite hacer esto y mientras agrega un elemento a HashSet, puede verificar si contiene el elemento o no.

HashSet<string> hashSet1 = new HashSet<string>(){"1","2","3"};
if (hashSet1.Add("1"))
   Console.WriteLine("'1' is successfully added to hashSet1!");
else
   Console.WriteLine("'1' could not be added to hashSet1, because it contains '1'");

HashSet tiene algunas funciones útiles, como IntersectWith, UnionWith, IsProperSubsetOf, ExceptWith, SymmetricExceptWithetc.

IsProperSubsetOf:

HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "4" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" };
HashSet<string> hashSet3 = new HashSet<string>() { "1", "2", "3", "4", "5" };
if (hashSet1.IsProperSubsetOf(hashSet3))
    Console.WriteLine("hashSet3 contains all elements of hashSet1.");
if (!hashSet1.IsProperSubsetOf(hashSet2))
    Console.WriteLine("hashSet2 does not contains all elements of hashSet1.");

UnionWith:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" };
hashSet1.UnionWith(hashSet2); //hashSet1 -> 3, 2, 4, 6, 8

IntersectWith:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4", "8" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" }
hashSet1.IntersectWith(hashSet2);//hashSet1 -> 4, 8

ExceptWith :

 HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "5", "6" };
 HashSet<string> hashSet2 = new HashSet<string>() { "1", "2", "3", "4" };
 hashSet1.ExceptWith(hashSet2);//hashSet1 -> 5, 6

SymmetricExceptWith :

 HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "5", "6" };
 HashSet<string> hashSet2 = new HashSet<string>() { "1", "2", "3", "4" };
 hashSet1.SymmetricExceptWith(hashSet2);//hashSet1 -> 4, 5, 6

Por cierto, el orden no se conserva en HashSets. En el ejemplo, agregamos el elemento "2" al final pero está en el segundo orden:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4", "8" };
hashSet1.Add("1");    // 3, 4, 8, 1
hashSet1.Remove("4"); // 3, 8, 1
hashSet1.Add("2");    // 3, 2 ,8, 1

51

A HashSet<T>es una clase diseñada para darle una O(1)búsqueda de contención (es decir, esta colección contiene un objeto en particular y me dice la respuesta rápidamente).

A List<T>es una clase diseñada para darle una colección con O(1)acceso aleatorio que puede crecer dinámicamente (piense en una matriz dinámica). Puede probar la contención a O(n)tiempo (a menos que la lista esté ordenada, entonces puede hacer una búsqueda binaria a O(log n)tiempo).

Tal vez pueda explicar con un ejemplo en qué casos se HashSet<T>debe preferirList<T>

Cuando quieres probar la contención en O(1).


Excepto que es O (log n) si la lista está ordenada; después de todo, es más rápido que buscar en una lista sin ordenar.
Andrei

20

Use a List<T>cuando quiera:

  • Almacene una colección de artículos en un orden determinado.

Si conoce el índice del elemento que desea (en lugar del valor del elemento en sí), la recuperación es O(1). Si no conoce el índice, la búsqueda del elemento lleva más tiempo O(n)para una colección sin clasificar.

Use a Hashset<T>cuando quiera:

  • Averigüe rápidamente si un determinado objeto está contenido en una colección.

Si conoce el nombre de la cosa que desea encontrar, la búsqueda es O(1)(esa es la parte 'Hash'). No mantiene un orden como lo List<T>hace y no puede almacenar duplicados (agregar un duplicado no tiene ningún efecto, esa es la parte 'Establecer').

Un ejemplo de cuándo usar un Hashset<T> sería si desea averiguar si una palabra jugada en un juego de Scrabble es una palabra válida en inglés (u otro idioma). Aún mejor sería si quisieras crear un servicio web para ser utilizado por todas las instancias de una versión en línea de dicho juego.

A List<T>sería una buena estructura de datos para crear el marcador para seguir los puntajes de los jugadores.


15

La lista es una lista ordenada. Es

  • Accedido por un índice entero
  • puede contener duplicados
  • tiene un orden predecible

HashSet es un conjunto. Eso:

  • Puede bloquear elementos duplicados (ver Agregar (T) )
  • No garantiza el orden de los artículos dentro del conjunto.
  • Tiene operaciones que esperaría en un conjunto, por ejemplo , IntersectWith, IsProperSubsetOf, UnionWith.

La lista es más apropiada cuando desea acceder a su colección como si fuera una matriz a la que podría agregar, insertar y eliminar elementos. HashSet es una mejor opción si desea tratar su colección como una "bolsa" de elementos en los que el orden no es importante o cuando desea compararlo con otros conjuntos utilizando las operaciones como IntersectWith o UnionWith.


3

La lista no es necesariamente única, mientras que el hashset sí lo es.


3

Una Lista es una colección ordenada de objetos de Tipo T que, a diferencia de una matriz, puede agregar y eliminar entradas.

Usaría una lista en la que desea hacer referencia a los miembros en el orden en que los almacenó y accede a ellos por una posición en lugar del elemento en sí.

Un HashSet es como un diccionario en el que el elemento en sí es la clave, así como el valor, el orden no está garantizado.

Usaría un HashSet donde desea verificar que un objeto esté en la colección


1
Para aclarar, en caso de que alguien más lo lea mal a primera vista, Listmantiene un orden (es decir, cuando se agregaron cosas), pero no clasifica automáticamente los elementos. Tendrías que llamar .Sorto usar a SortedList.
drzaus

1

Si decide aplicar estas estructuras de datos al uso real en el desarrollo basado en datos, un HashSet es MUY útil para probar la replicación contra fuentes de adaptador de datos, para la limpieza y migración de datos.

Además, si se usa la clase DataAnnotations, se puede implementar la lógica clave en las propiedades de la clase y controlar efectivamente un índice natural (agrupado o no) con un HashSet, donde esto sería muy difícil en una implementación de la lista.

Una opción sólida para usar una lista es implementar genéricos para múltiples medios en un Modelo de vista, como enviar una lista de clases a una Vista MVC para un Ayudante DropDownList, y también para enviar como una construcción JSON a través de WebApi. La lista permite la lógica típica de recopilación de clases y mantiene la flexibilidad para un enfoque más similar a la "Interfaz" para calcular un modelo de vista única para diferentes medios.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.