Qual è la differenza tra HashSet <T> e List <T>?


156

Puoi spiegare qual è la differenza tra HashSet<T>e List<T>in .NET?

Forse puoi spiegare con un esempio in quali casi HashSet<T>dovrebbe essere preferito List<T>?



Ti consiglio di consultare gli articoli di Wikipedia su en.wikipedia.org/wiki/Hash_table e en.wikipedia.org/wiki/Dynamic_array .
mqp,

Per quanto riguarda le prestazioni, vedere hashset-vs-list-performance
nawfal,

Risposte:


213

A differenza di un elenco <> ...

  1. Un HashSet è un elenco senza membri duplicati.

  2. Poiché un HashSet è vincolato a contenere solo voci univoche, la struttura interna è ottimizzata per la ricerca (rispetto a un elenco): è notevolmente più veloce

  3. L'aggiunta a un HashSet restituisce un valore booleano: false se l'addizione non riesce a causa della già esistente in Set

  4. Può eseguire operazioni su set matematici su un set: Union / Intersezione / IsSubsetOf ecc.

  5. HashSet non implementa IList solo ICollection

  6. Non è possibile utilizzare gli indici con un HashSet, solo gli enumeratori.

Il motivo principale per utilizzare un HashSet sarebbe se sei interessato a eseguire operazioni Set.

Dato 2 set: hashSet1 e hashSet2

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

vola in confronto con un'operazione equivalente usando LINQ. È anche più carino scrivere!


IDK, ho avuto problemi con il Unionmetodo. Avevo usato UnionWithinvece.
utente

2
+1 per "Il motivo principale per utilizzare un HashedSet sarebbe se sei interessato a eseguire operazioni Set."
LCJ,

12
In realtà, preferisco la risposta che sottolinea che gli Hashset sono adatti nei casi in cui puoi trattare la tua collezione come "articoli bag". Le operazioni impostate non sono così frequenti come i controlli di contenimento. In qualsiasi momento in cui si dispone di un set di elementi univoci (ad esempio codici) e è necessario controllare il contenimento, un HashSet è utile.
ThunderGr

Buona risposta. Sono tentato di aggiungere anche alcune differenze caratteristiche prestazionali.
nawfal,

1
Domanda: il motivo principale è non avere la certezza di non avere elementi duplicati?
Andrea Scarafoni,

54

Per essere più precisi, dimostriamo con esempi,

Non è possibile utilizzare HashSet come nell'esempio seguente.

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

hashSet1[i] produrrebbe un errore:

Impossibile applicare l'indicizzazione con [] a un'espressione di tipo "System.Collections.Generic.HashSet"

Puoi usare foreach statement:

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

Non è possibile aggiungere elementi duplicati a HashSet mentre Elenco consente di farlo e mentre si aggiunge un elemento a HashSet, è possibile verificare se contiene o meno l'elemento.

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 ha alcune funzioni utili come 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

A proposito, l'ordine non viene conservato negli Hashset. Nell'esempio, abbiamo aggiunto l'ultimo elemento "2" ma è nel secondo ordine:

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>è una classe progettata per darti la O(1)ricerca del contenimento (ovvero, questa raccolta contiene un oggetto particolare e dimmi velocemente la risposta).

A List<T>è una classe progettata per offrirti una raccolta con O(1)accesso casuale che può crescere in modo dinamico (pensa ad array dinamici). Puoi testare il contenimento nel O(n)tempo (a meno che l'elenco non sia ordinato, quindi puoi fare una ricerca binaria nel O(log n)tempo).

Forse puoi spiegare con un esempio in quali casi HashSet<T>dovrebbe essere preferitoList<T>

Quando si desidera testare il contenimento in O(1).


Tranne che è O (log n) se l'elenco è ordinato; dopo tutto, è più veloce della ricerca in un elenco non ordinato.
Andrei,

20

Usa a List<T>quando vuoi:

  • Conservare una raccolta di articoli in un determinato ordine.

Se si conosce l'indice dell'oggetto desiderato (anziché il valore dell'articolo stesso) è il recupero O(1). Se non si conosce l'indice, la ricerca dell'elemento richiede più tempo, O(n)per una raccolta non ordinata.

Usa a Hashset<T>quando vuoi:

  • Scopri rapidamente se un determinato oggetto è contenuto in una raccolta.

Se conosci il nome della cosa che vuoi trovare, Lookup è O(1)(questa è la parte 'Hash'). Non mantiene un ordine come List<T>fa e non è possibile archiviare i duplicati (l'aggiunta di un duplicato non ha alcun effetto, questa è la parte 'Imposta').

Un esempio di quando usare a Hashset<T>sarebbe se vuoi scoprire se una parola giocata in una partita di Scrabble è una parola valida in inglese (o in un'altra lingua). Ancora meglio sarebbe se si volesse creare un servizio Web da utilizzare per tutte le istanze di una versione online di un gioco del genere.

A List<T>sarebbe una buona struttura di dati per la creazione del tabellone segnapunti per tenere traccia dei punteggi dei giocatori.


15

Elenco è un elenco ordinato. È

  • accessibile da un indice intero
  • può contenere duplicati
  • ha un ordine prevedibile

HashSet è un set. It:

  • Può bloccare elementi duplicati (vedi Aggiungi (T) )
  • Non garantisce l'ordine degli articoli all'interno del set
  • Ha operazioni che ti aspetteresti da un set, ad esempio IntersectWith, IsProperSubsetOf, UnionWith.

L'elenco è più appropriato quando si desidera accedere alla propria raccolta come se fosse come un array a cui è possibile aggiungere, inserire e rimuovere elementi. HashSet è una scelta migliore se si desidera trattare la propria raccolta come una "borsa" di articoli in cui l'ordine non è importante o quando si desidera confrontarlo con altri set utilizzando operazioni come IntersectWith o UnionWith.



3

Una lista è una raccolta ordinata di oggetti di tipo T che a differenza di un array è possibile aggiungere e rimuovere voci.

Dovresti utilizzare un elenco in cui desideri fare riferimento ai membri nell'ordine in cui li hai archiviati e accedi a loro da una posizione anziché dall'elemento stesso.

Un HashSet è come un dizionario che l'articolo stesso è la chiave e il valore, l'ordinamento non è garantito.

Dovresti usare un HashSet dove vuoi controllare che un oggetto sia nella collezione


1
Per chiarire, nel caso in cui qualcun altro legga ciò che è sbagliato a prima vista, Listmantiene un ordine (cioè quando le cose sono state aggiunte), ma non ordina automaticamente gli articoli. Dovresti chiamare .Sorto usare un SortedList.
drzaus,

1

Se si decide di applicare queste strutture dati all'utilizzo effettivo nello sviluppo basato sui dati, un HashSet è MOLTO utile nel testare la replica su origini dell'adattatore dati, per la pulizia e la migrazione dei dati.

Inoltre, se si utilizza la classe DataAnnotations, è possibile implementare la logica chiave sulle proprietà della classe e controllare efficacemente un indice naturale (raggruppato o meno) con un HashSet, dove ciò sarebbe molto difficile in un'implementazione dell'elenco.

Un'opzione efficace per l'utilizzo di un elenco è l'implementazione di generici per più supporti su un modello di visualizzazione, come l'invio di un elenco di classi a una vista MVC per un Helper DropDownList e anche per l'invio come costrutto JSON tramite WebApi. L'elenco consente la tipica logica di raccolta di classi e mantiene la flessibilità per un approccio più "interfaccia" come il calcolo di un modello a vista singola su supporti diversi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.