View Full Version : [JAVA] String e equals
redcloud
12-12-2007, 16:01
Perche equals serve a confrontare due stringhe quando esiste compareTo? Forse perchè le stringhe sono oggetti immutabili e hanno preferito sfruttare anche il metodo equals per effettuare il confronto altrimenti non avrebbe avuto senso?
mad_hhatter
12-12-2007, 16:38
Perche equals serve a confrontare due stringhe quando esiste compareTo? Forse perchè le stringhe sono oggetti immutabili e hanno preferito sfruttare anche il metodo equals per effettuare il confronto altrimenti non avrebbe avuto senso?
equals dovrebbe ritornare true se e solo se compareTo ritorna 0...
compareTo è un operatore di ordinamento, equals è un operatore di uguaglianza,
puoi usare quello che vuoi, però è più comodo scrivere string1.equals(string2) piuttosto che string1.compareTo(string2) == 0
inoltre tu potresti voler ridefinire l'operatore equals, ma tenere compareTo immutato
redcloud
12-12-2007, 16:49
equals dovrebbe ritornare true se e solo se compareTo ritorna 0...
compareTo è un operatore di ordinamento, equals è un operatore di uguaglianza,
puoi usare quello che vuoi, però è più comodo scrivere string1.equals(string2) piuttosto che string1.compareTo(string2) == 0
inoltre tu potresti voler ridefinire l'operatore equals, ma tenere compareTo immutato
Si lo so, era solo una curiosità del perchè hanno fatto questa scelta, e la mia motivazione mi sembra quella più logica.
mad_hhatter
12-12-2007, 23:45
Si lo so, era solo una curiosità del perchè hanno fatto questa scelta, e la mia motivazione mi sembra quella più logica.
in realta' non vedo perche' l'essere immutabili influisca sull'uso del metodo equals... equals e compareTo li ho sempre visti in coppia (per ogni classe che implementa un ordinamento tra le sue istanze) e il motivo e' quello che ti ho scritto nella prima risposta.
Perche equals serve a confrontare due stringhe quando esiste compareTo? Forse perchè le stringhe sono oggetti immutabili e hanno preferito sfruttare anche il metodo equals per effettuare il confronto altrimenti non avrebbe avuto senso?No.
Innanzitutto equals() è definito nella classe Object. Ogni sottoclasse di Object quindi ha la possibilità di fare l'override di equals() per definire in modo più corretto come stabilire se due oggetti sono "meaningfully equivalent" (di significato equivalente). E se si fa l'override di equals() si dovrebbe anche fare l'override di hashCode(). I due metodi sono legati tra di loro da un "contratto" ben preciso. Se si implementa equals() ma non hashCode() tale contratto non verrebbe più rispettato.
compareTo() invece ha un obiettivo diverso ed è legato all'interfaccia Comparable. Mai notato che se una classe ha il compareTo(), è anche Comparable??
Comparable e il metodo compareTo() servono tipicamente per determinare un ordine tra due oggetti. L'ordine fornito da un Comparable viene chiamato il "natural ordering" (ordinamento naturale). Ma è anche possibile definire N altri ordinamenti specifici implementando l'interfaccia Comparator in apposite classi separate.
Quindi, come vedi, equals() e compareTo() hanno obiettivi diversi. E infine, nota, la documentazione di compareTo() dice:
It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)).
Quindi potrebbe anche capitare che equals() e compareTo() non abbiano alcuna relazione tra di loro.
redcloud
13-12-2007, 15:46
No.
Innanzitutto equals() è definito nella classe Object. Ogni sottoclasse di Object quindi ha la possibilità di fare l'override di equals() per definire in modo più corretto come stabilire se due oggetti sono "meaningfully equivalent" (di significato equivalente). E se si fa l'override di equals() si dovrebbe anche fare l'override di hashCode(). I due metodi sono legati tra di loro da un "contratto" ben preciso. Se si implementa equals() ma non hashCode() tale contratto non verrebbe più rispettato.
compareTo() invece ha un obiettivo diverso ed è legato all'interfaccia Comparable. Mai notato che se una classe ha il compareTo(), è anche Comparable??
Comparable e il metodo compareTo() servono tipicamente per determinare un ordine tra due oggetti. L'ordine fornito da un Comparable viene chiamato il "natural ordering" (ordinamento naturale). Ma è anche possibile definire N altri ordinamenti specifici implementando l'interfaccia Comparator in apposite classi separate.
Quindi, come vedi, equals() e compareTo() hanno obiettivi diversi. E infine, nota, la documentazione di compareTo() dice:
It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)).
Quindi potrebbe anche capitare che equals() e compareTo() non abbiano alcuna relazione tra di loro.
Si questo si sapeva. Mi ero solo posto l'interrogativo. Mi hanno sempre insegnato che per comparare due oggetti si deve usare il compareTo, per verificare l'uguaglianza tra due oggetti si deve usare equals invece. Sulla maggior parte degli oggetti che ereditano Object, il metodo equals confronta soltanto gli indirizzi. Caso particolare la classe String che fa assumere al metodo equals e compareTo == 0 lo stesso significato. Perchè hanno fatto questa scelta? A mio avviso perchè dato che di default il metodo equals serve a confrontare due indirizzi, e dato che sugli oggetti String questa operazione non avrebbe senso in quanto oggetti immutabili, hanno preferito sfruttare equals per fare il confronto come per compareTo. Tutto qua. Per confrontare due oggetti io preferisco usare il compareTo, equals lo uso solo per confrontare se due riferimenti puntano allo stesso oggetto. Infatti ci sono rimasto male quando ho scoperto che
"ABC".equals("ABC")
restituisce true
Sulla maggior parte degli oggetti che ereditano Object, il metodo equals confronta soltanto gli indirizzi.Se equals() non viene ridefinito, sì. equals() in Object, non potendo fare in altro modo, si limita solo a confrontare i due reference.
Ma in una sottoclasse che ha un qualche stato è buona norma ridefinire correttamente equals() altrimenti due oggetti diversi ma con lo stesso identico stato non verrebbero visto come "uguali".
Caso particolare la classe String che fa assumere al metodo equals e compareTo == 0 lo stesso significato. Perchè hanno fatto questa scelta? A mio avviso perchè dato che di default il metodo equals serve a confrontare due indirizzi, e dato che sugli oggetti String questa operazione non avrebbe senso in quanto oggetti immutabili, hanno preferito sfruttare equals per fare il confronto come per compareTo.Non vedo alcun caso particolare e non centra nulla la immutabilità.
String ha uno stato e quindi equals() viene ridefinito in modo che non si basi solamente sui reference. In più implementa Comparable e viene fatto in modo che la uguaglianza coincida per equals() e compareTo().
Come hai letto nella nota che ho riportato, è raccomandato ma non strettamente obbligatorio/richiesto. Non so dirti se nel framework ci sono delle classi che non fanno coincidere l'uguaglianza per equals() e compareTo().
Se non devi fare cose davvero particolari o "strane" è bene far coincidere l'uguaglianza.
equals lo uso solo per confrontare se due riferimenti puntano allo stesso oggetto.equals() NON serve per confrontare i reference. Serve per stabilire se due oggetti sono di significato (contenuto) equivalente.
Infatti ci sono rimasto male quando ho scoperto che
"ABC".equals("ABC")
restituisce trueNon capisco lo stupore. equals() di String utilizza il contenuto delle stringhe per stabilire se sono uguali.
Tra l'altro, le due stringhe che hai usato sopra sono stringhe literal uguali. Il compilatore lo capisce e ne mette solo una nel constant-pool. Quindi quelle due stringhe hanno lo stesso identico reference. Ma ti dico di più. equals() di String innanzitutto verifica se this == oggetto_passato. Quindi nel caso particolare sopra ritorna subito true senza nemmeno vedere il contenuto delle stringhe.
^TiGeRShArK^
13-12-2007, 18:07
Se equals() non viene ridefinito, sì. equals() in Object, non potendo fare in altro modo, si limita solo a confrontare i due reference.
Si, ma se non ricordo male che confronti gli indirizzi non è garantito dalle specifiche java.
infatti:
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
Ad esempio, come diceva giustamente il buon vecchio pgi un pò di tempo fa, su una VM a 64 bit l'hashcode non può essere un indirizzo dato che un int è a 64 bit mentre un'indirizzo è a 64 bit.
Cmq solitamente nelle implementazioni a 32 bit viene usato il reference per generare l'hashcode.
Si, ma se non ricordo male che confronti gli indirizzi non è garantito dalle specifiche java.
infatti:
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)E cosa centra questo con equals()??? (prima si stava parlando di equals() .....)
Ciò che hai riportato vale per hashCode(), che è più particolare perché deve ritornare un int. Anche in questo caso, nella classe Object non è che si possa usare chissà che cosa e infatti tipicamente l'intero ritornato è basato sull'indirizzo interno dell'oggetto. E la nota spiega bene la questione.
Ad esempio, come diceva giustamente il buon vecchio pgi un pò di tempo fa, su una VM a 64 bit l'hashcode non può essere un indirizzo dato che un int è a 64 bit mentre un'indirizzo è a 64 bit.In Java un int è sempre a 32 bit, indipendentemente dalla piattaforma. Se poi l'architettura è a 64 bit e gli indirizzi sono appunto a 64 bit, per il programmatore Java non cambia nulla.
Il metodo hashCode() in Object è marcato "native", non so esattamente cosa faccia (bisognerebbe vedere i sorgenti nativi). Su una architettura a 64 bit, se dovessi farlo io, farei in modo che hashCode() prenda l'indirizzo a 64 bit nativo e ne faccia lo XOR della parte alta e bassa per ottenere un valore a 32 bit da far ritornare. E magari funziona proprio così .... (non lo so). :p
^TiGeRShArK^
13-12-2007, 20:51
Il metodo hashCode() in Object è marcato "native", non so esattamente cosa faccia (bisognerebbe vedere i sorgenti nativi). Su una architettura a 64 bit, se dovessi farlo io, farei in modo che hashCode() prenda l'indirizzo a 64 bit nativo e ne faccia lo XOR della parte alta e bassa per ottenere un valore a 32 bit da far ritornare. E magari funziona proprio così .... (non lo so). :p
mmm...
non lo so..
se per pure sfiga ti capita la parte bassa e alta di un oggetto uguali ma scambiate hai lo stesso hashcode :p
sinceramente non ho idea di come sia implementato e di guardarmi i sorgenyi della VM non ne ho proprio voglia dato che x oggi la mia dose quotidiana non voluta di c me la sono dovuta sorbire (maledetto linux :muro: ) :p
se per pure sfiga ti capita la parte bassa e alta di un oggetto uguali ma scambiate hai lo stesso hashcode :pE allora?? hashCode() non è fatto per fornire un valore univoco per ogni oggetto!
La cosa che è veramente importante è che i valori che si possono ottenere da hashCode() siano molto ben "sparpagliati".
Se poi ci sono delle collisioni, chissenefrega (più o meno... ;) ). hashCode() viene usato tipicamente nelle hash-table (java.util.HashMap e simili). Se c'è collisione vuol solo dire che oggetti diversi ma con lo stesso hashcode andranno certamente a finire nello stesso "bucket" (anzi ... è anche possibile che oggetti con hashcode diversi vadano lo stesso a finire nello stesso bucket). Sotto un bucket ci può essere una lista di N oggetti. La lista dovrà essere scansionata e l'oggetto andrà cercato basandosi su equals().
mad_hhatter
17-12-2007, 16:37
E allora?? hashCode() non è fatto per fornire un valore univoco per ogni oggetto!
La cosa che è veramente importante è che i valori che si possono ottenere da hashCode() siano molto ben "sparpagliati".
Se poi ci sono delle collisioni, chissenefrega (più o meno... ;) ). hashCode() viene usato tipicamente nelle hash-table (java.util.HashMap e simili). Se c'è collisione vuol solo dire che oggetti diversi ma con lo stesso hashcode andranno certamente a finire nello stesso "bucket" (anzi ... è anche possibile che oggetti con hashcode diversi vadano lo stesso a finire nello stesso bucket). Sotto un bucket ci può essere una lista di N oggetti. La lista dovrà essere scansionata e l'oggetto andrà cercato basandosi su equals().
potresti esplicitare quel "più o meno" riferito al chissenefrega?
grazie mille
potresti esplicitare quel "più o meno" riferito al chissenefrega? Se sei tu che devi implementare hashCode() è chiaro che non puoi "fregartene" delle collisioni. Devi realizzare un buon algoritmo di hash che minimizzi le collisioni. E come farlo dipende ovviamente da quanti/quali dati hai come "stato" nella tua classe.
mad_hhatter
17-12-2007, 17:44
Se sei tu che devi implementare hashCode() è chiaro che non puoi "fregartene" delle collisioni. Devi realizzare un buon algoritmo di hash che minimizzi le collisioni. E come farlo dipende ovviamente da quanti/quali dati hai come "stato" nella tua classe.
mmm... ti spiego qual è il mio problema.
ho una classe RfidTag che contiene il membro long id. Io voglio che per una Hashtable<RfidTag, Blabla> il metodo containsKey(tag) ritorni true se nella table c'è una key tag_1 avente lo stesso id di tag anche se tag e tag_1 sono oggetti distinti. Avevo ridefinito il metodo equals di RfidTag, ma poi mi sono accorto, leggendo il sorgente del metodo containsKey(), che oltre a equals viene testato anche l'hashCode. Allora ho ridefinito il metodo hashCode in RfidTag facendo lo xor della prima metà dell'id con la seconda metà... però mi viene il dubbio che questo possa avere degli effetti collaterali pur rispettando il contratto del metodo hashCode.
Dove posso trovare info su buoni algoritmi di hash?
grazie mille per l'aiuto
PS: a che scopo dobbiamo cercare di minimizzare le collisioni? per motivi prestazionali o logico-semantici?
Allora ho ridefinito il metodo hashCode in RfidTag facendo lo xor della prima metà dell'id con la seconda metà... però mi viene il dubbio che questo possa avere degli effetti collaterali pur rispettando il contratto del metodo hashCode.Hai fatto bene. Se ti può tranquillizzare, la classe Long fa proprio la stessa identica cosa: XOR tra la parte alta e la parta bassa del valore. ;)
Dove posso trovare info su buoni algoritmi di hash?Non saprei dirti. Però inizia a guardare a partire da <qui> (http://en.wikipedia.org/wiki/Hash_function).
PS: a che scopo dobbiamo cercare di minimizzare le collisioni? per motivi prestazionali o logico-semantici?Per motivi di prestazioni.
Un caso limite che in genere viene usato per spiegare questa questione è un hashCode() che restituisce un valore costante:
public int hashCode () {
return 123;
}Sarebbe perfettamente legale e non violerebbe alcun contratto. Peccato che sarebbe spaventosamente inefficiente. Immagina che succederebbe se gli oggetti della classe che ha questo hashCode() venissero usati come chiavi in una, parlando in generale, "hash-table". Tutti gli oggetti finirebbero nello stesso medesimo "bucket".
Con un valore costante in pratica ci sarebbe sempre al 100% una "collisione".
mad_hhatter
18-12-2007, 08:49
molte grazie andbin!
lo xor l'ho usato perché ne avevi parlato tu qualche post più su... mi fa piacere di aver azzeccato la tecnica giusta :)
grazie infinite!
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.