2011-12-16 13 views
7

ha un costruttore che accetta un comparatore, vale a dire anche se gli oggetti che si memorizzano non sono oggetti Comparable da soli, è possibile fornire un comparatore personalizzato.TreeSet/TreeMap equivalente per HashSet/HashMap (hasher personalizzato)

Esiste un'implementazione analoga di un set non ordinato? (Ad esempio, in alternativa al HashSet<T> che prende un oggetto "hasher" che calcola equals() e hashCode() per gli oggetti T che può essere diverso da proprie implementazioni degli oggetti?)

C++ std::hash_set questo dà, chiedo solo se c'è qualcosa per Java .


Edit: @Max porta in primo piano un buon punto di tecniche relative equals() - abbastanza giusto; ed è vero per le chiavi TreeMap e HashMap tramite Map.containsKey(). Ma ci sono altre strutture di dati ben note là fuori che consentono l'organizzazione da parte degli organizzatori?

+0

A proposito, sei sicuro di non stia mescolando i domini degli oggetti diversi? Generalmente non hai problemi ad aggiungere nuovi metodi agli oggetti che sono nel dominio della tua applicazione. Se, tuttavia, stai provando a creare una mappa di oggetti ricevuti da un client Axis generato (ad esempio), allora stai mescolando domini diversi: dominio di WebService e dominio della tua Applicazione. Il che significa che in sostanza non dovresti mai aver bisogno di ciò che stai chiedendo. – bezmax

risposta

9

No, la presenza di un oggetto "hasher" non è supportata dalle specifiche Collections. Puoi certamente implementare la tua raccolta che supporta questo, ma un altro modo per farlo è considerare lo Hasher come un oggetto wrapping archiviato nel tuo HashSet.

Set<HasherWrapper<Foo>> set = new HashSet<HasherWrapper<Foo>>(); 
set.add(new HasherWrapper(foo)); 
... 

La classe wrapper sarebbe quindi cercare qualcosa di simile:

private class HasherWrapper<T> { 
    T wrappedObject; 
    public HasherWrapper(T wrappedObject) { 
     this.wrappedObject = wrappedObject; 
    } 
    @Override 
    public int hashCode() { 
     // special hash code calculations go here 
    } 
    @Override 
    public boolean equals(Object obj) { 
     // special equals code calculations go here 
    } 
} 
+1

Bella soluzione alla domanda. Un suggerimento, dovresti rendere finale 'wrappedObject'. Inoltre, 'wrappedObject' dovrebbe essere pubblico o privato con un getter. –

+0

Perchè finale? Questo aiuta davvero molto con l'hotspot? Dato che è una classe privata, consentirei alla classe esterna di accedere al campo senza un getter. – Gray

+0

Un numero di riferimenti Web dice che la finale non è necessaria in questi giorni perché le macchine virtuali sono intelligenti e sanno se qualcosa è stato sovrascritto. http://stackoverflow.com/questions/4279420/does-use-of-final-keyword-in-java-improve-the-performance – Gray

0

Non c'è niente del genere, hashcode() e equals() definiscono gli attributi di un oggetto e non devono essere modificati. Definiscono ciò che rende un oggetto uguale l'uno all'altro e questo non dovrebbe essere diverso da un set all'altro. L'unico modo per fare ciò di cui si sta parlando è sottoclasse l'oggetto e scrivere un nuovo hashcode() e equals() e questo avrebbe davvero senso solo se la sottoclasse avesse una variabile di definizione che andrebbe aggiunta oltre alla super classe 'hashcode() e equals(). So che questo potrebbe non essere ciò a cui miri, ma spero che questo aiuti. Se spieghi il tuo ragionamento in più per volerlo, potrebbe essere utile trovare una soluzione migliore se ce n'è una.

+5

In realtà, come si decide di eseguire l'hashing e confrontare gli oggetti nel programma non si definiscono gli attributi degli oggetti, ma del programma. Non c'è niente di sbagliato nel voler utilizzare un diverso algoritmo di hash che si conosce è meglio per il proprio set di problemi particolare, o considerare gli oggetti uguali se hanno lo stesso nome, o lo stesso id, o qualcos'altro. Dipende dal programma, e questa è un'area in cui il design di Java lascia molto a desiderare. –

1

No, non c'è e non ci può essere secondo le specifiche. Inoltre, hai frainteso il modo in cui TreeSet utilizza il numero Comparator.

Da TreeSet Javadoc:

Si noti che l'ordinamento gestito da un insieme (anche se non un esplicito comparatore è fornito) deve essere coerente con eguali se si tratta di correttamente implementare l'interfaccia Set. (Vedi Comparable o Comparator per una definizione precisa di coerente con equals.) Questo è così perché l'interfaccia Set è definita in termini dell'operazione equals, ma un'istanza TreeSet esegue tutti i confronti degli elementi utilizzando il suo confronto (o confronta) metodo, quindi due elementi che sono considerati uguali con questo metodo sono, dal punto di vista del set, uguali. Il comportamento di di un set è ben definito anche se il suo ordinamento è incoerente con uguale; semplicemente non riesce a rispettare il contratto generale dell'interfaccia Set .

Da Comparable javadoc:

L'ordinamento naturale per una classe C si dice che sia coerente con uguale se e solo se e1.compareTo (e2) == 0 ha lo stesso valore booleano come e1.equals (e2) per ogni e1 ed e2 della classe C. Si noti che null non è un'istanza di qualsiasi classe e e.compareTo (null) deve generare un valore NullPointerException anche se e.equals (null) restituisce false.

Da Collection javadoc:

booleana contiene (Object o)

restituisce true se questa collezione contiene l'elemento specificato. Più formalmente, restituisce true se e solo se questa raccolta contiene almeno un elemento e tale che (o == null? e == null: o.equals (e)).

Pertanto, precisando non ci può essere qualsiasi tipo di classe che implementa Collection<E> interfaccia e completamente dipendono da un oggetto Comparator-stile esterno per inserire gli oggetti. Tutte le raccolte devono utilizzare il metodo equals di una classe Object per verificare se l'oggetto è già inserito.

+0

Sto cercando di capire perché non è possibile fornire un comparatore personalizzato diverso che sia coerente con gli uguali. Non e2.compareTo (e1) raggiunge questo obiettivo (inverte semplicemente l'ordine, ma gli elementi uguali sono uguali)? –

+0

Perché la specifica dell'interfaccia di raccolta dipenderà dall'implementazione dei programmatori di Comparatore con nome. L'interfaccia di raccolta afferma esplicitamente che ** tutte ** delle sue implementazioni considereranno gli oggetti uguali se 'a.equals (b) '. Se implementi una sorta di Raccolta che ignora o ignora parzialmente tale regola (come nel caso specificato dall'autore), violerai le specifiche dell'interfaccia di Raccolta. Anche se scrivi nella documentazione "Il comparatore passato ** deve ** essere coerente con gli uguali !!" - le specifiche rimarranno ancora infrante. – bezmax

4

Non esiste un'implementazione di questo tipo nella libreria standard, ma non impedisce il rollover. Questo è qualcosa che ho spesso voluto avere me stesso.

Vedere http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4771660 per la ragione:

Volevamo evitare la complessità. Abbiamo seriamente preso in considerazione questa nozione nel momento in cui il framework delle collezioni è stato progettato, ma lo abbiamo respinto. La razione di incremento del peso di sembrava bassa. Abbiamo ritenuto che fosse uguale a quello che hai desiderato il per il 95% delle volte; ==, 4%; e qualcos'altro 1%. Scrivi contratti ragionevoli per operazioni di massa quando è molto difficile quando i predicati di uguaglianza differiscono.