2012-05-17 13 views
6

Il C# il generico HashSet < T> prestazioni di ricerca dovrebbe essere O (1), e le prestazioni di ricerca di una ObservableCollection < T> dovrebbe essere O (n).C# HashSet <T> prestazioni di ricerca (rispetto a ObservableCollection <T>)?

Ho una grande quantità di elementi unici, ogni elemento ha una proprietà DateTime che non è univoca.

Ogni elemento calcola il proprio HashCode semplicemente restituendo il suo DateTime.GetHashCode().

Ora voglio ottenere un sottoinsieme dei miei dati, ad es. tutti gli elementi che hanno una data che si trova tra marzo 2012 e giugno 2012.

var result = from p in this.Elements 
       where p.Date >= new DateTime(2012, 03, 01) && 
         p.Date <= new DateTime(2012, 30, 06 
       select p; 

Se corro questa query LINQ su una collezione di 300.000 elementi, ci vuole ~ 25 ms per tornare 80 elementi che si trovano all'interno della gamma data - Non importa se utilizzo un HashSet < T> o ObservableCollection < T>.

Se faccio scorrere tutti gli elementi manualmente e li controllo, richiede lo stesso tempo, ~ 25 ms.

Ma conosco il codice hash di tutte le date che rientrano nell'intervallo specificato. È possibile ottenere tutti gli elementi con gli HashCode forniti dal mio HashSet < T>? Penso che sarebbe molto più veloce ...

È possibile velocizzare la query LINQ? Suppongo che non sfrutti le abilità speciali del mio HashSet < T>?

+0

Il codice hash di ogni elemento è la data? – Jodrell

+0

Non ci sono abilità speciali di un HashSet che consentirà il recupero efficiente di elementi la cui data rientra in un intervallo. Un HashSet consente di determinare rapidamente se un determinato oggetto o valore è (o non è) nell'insieme. – hatchet

+0

La mia prima osservazione è che i codici hash dovrebbero essere diversi, se possibile, se gli oggetti differiscono (questo chiaramente non può essere sempre il caso, ma è ciò che si intende perseguire). Nel tuo caso questo non è il caso. Hai diversi elementi con hashcode identici che è male. Nel peggiore dei casi, se hai solo tre diverse date uniche, il tuo hashset avrà solo tre bucket e quindi trovare qualcosa nell'hashset dovrà ordinare tutti gli elementi in quel bucket che lo portano ad essere O (n) (dare o prendere). Dovrei anche notare che questa è una nota generale, non direttamente correlata alle domande :) – Chris

risposta

4

Come è stato sottolineato, un set di hash è molto efficiente nel determinare se un determinato hash è nell'insieme. La tua query utilizza semplicemente il fatto che l'hashset implementa IEnumerable per iterare sull'intero insieme e fare il confronto della data. Non userà affatto gli hash. Questo è il motivo per cui la modalità manuale ha lo stesso tempo della query.

Non è possibile ottenere un elemento basato su un hash da un hashset, è possibile solo verificare l'esistenza dell'elemento nel set. Un dizionario è quello che vuoi se hai bisogno di ottenerlo (che sembra che tu non lo sia)

Decidi cosa devi fare con i tuoi dati e usa una struttura che sia ottimizzata per quello. Questa potrebbe essere la tua classe che mantiene più strutture interne ognuna delle quali è efficiente in una cosa (come una per la ricerca di intervalli e un'altra per la verifica dell'esistenza di più campi), oppure potrebbe esserci una struttura esistente che si adatta alle tue esigenze. Ma senza sapere cosa vuoi fare con i tuoi dati è difficile da consigliare.

L'altra cosa da considerare è se si sta ottimizzando prematuramente. Se 25 ms per la ricerca manuale è abbastanza veloce allora forse qualsiasi struttura che implementa IEnumerable sarà abbastanza buono. Nel qual caso puoi sceglierne uno in base agli altri criteri di cui hai bisogno.

+0

Grazie per la risposta. Penso che le attuali prestazioni di ricerca siano più che sufficienti, ho solo pensato che fosse possibile recuperare gli elementi direttamente tramite il loro codice hash, che è come non hai indicato. Il metodo Remove di 'HashSet ' è molto più performante di quello offerto da una raccolta "normale", quindi userò sicuramente un HashSet. – Ehssan

4

Non stai utilizzando la giusta struttura dati. Dovresti usare qualcosa come una lista ordinata (ordinata sulla proprietà Date) dove puoi quindi effettuare una ricerca binaria per l'inizio e la fine dell'intervallo.

+2

Oppure un albero di ricerca binario :) – undefined

+0

Sì, utilizzerei sicuramente un SortedList o SortedDicionary, ma non posso - la 'Date' dell'elemento non è una chiave univoca ... – Ehssan

+0

@EhssanDoust perché il fatto che la data non essere unici ti impedisce di usare un dizionario? Finché il metodo Equals determina correttamente quando 2 istanze sono uguali e il codice gethash restituisce sempre lo stesso valore per 2 oggetti diversi se uguali tra questi oggetti è anche true, allora funzionerà. –

Problemi correlati