hashCode bei Objekten

EG2009

Mitglied
Hallo Leute,

Nach stundenlangem Beschäftigen mit dem Thema hashCode bin ich immernoch nicht ganz sicher, ob ich manche Dinge richtig interpretiere.


Wenn ich 2 Objekte einer Klasse erzeuge und die Instanzvariablen in beiden Objekten auf exakt dieselben Werte setze, dann sind die Objekte für mich inhaltlich identisch.

Folgenden Code habe ich eben mal kurz gekritzelt:

Java:
public class ObjekteTest2 {

String titel;
int laenge;

	public static void main(String[] args) {
		ObjekteTest o1 = new ObjekteTest();
		o1.setTitel("hallo");
		o1.setLaenge(23);
		ObjekteTest o2 = new ObjekteTest();
		o2.setTitel("hallo");
		o2.setLaenge(23);
		ObjekteTest o3 = o2;
		
		System.out.println(o1.titel + " " + o1.laenge + " " + o1.hashCode() + " " + o2.titel + " " + 

o2.laenge + " " + o2.hashCode() + " " + o3.titel + " " + o3.laenge + " " + o3.hashCode());

	} // End main

	
		public void setTitel(String s) {
			titel = s;
		}


		public void setLaenge(int zahl) {
			laenge = zahl;
		}
	
} // End class


Frage:
Ist es korrekt, dass ich hier für die beiden Objekte o1 und o2 unterschiedliche Hashcodes erhalte?! Die Objekte sind inhaltlich gleich, aber erzeugen unterschiedliche Hashcodes. Wird der Hashcode dann abhängig vom Speicherort erzeugt, oder wie läuft die Kiste?

Bin grad leider etwas verwirrt ???:L


Wäre super, wenn ihr etwas Licht ins Dunkel bringen könntet.

Besten Dank!

Seb
 
Nur du kannst wissen, ob zwei Objekte für dich "inhaltlicht" dieselben sind, deshalb werden standardmäßig allen Objekten verschiedene Hashcodes zugeordnet. Wenn du andere Vorstellungen hast, musst du die equals() und hashCode()-Methoden selbst überschreiben, so dass der Vergleich bzw. der Hashcode genau auf den (für dich) "relevanten" Feldern basiert.

Das Thema ist subtil. Ein paar Beispiele:
-Timestamp erbt von java.util.Date. Sollen ein Timestamp und ein Date, die auf die gleiche Zeit zeigen, "gleich" sein und den gleichen Hashcode bekommen?
- Ist new BigDecimal("2") gleich new BigDecimal("2.000")? (Java sagt nein)
- Sind zwei Excptions mit gleichen Messages und Stacktrace "gleich", auch wenn sie zu unterschiedlichen Zeiten auftreten?
 
"inhaltlich identisch" klingt nach einem etwas widersprüchlichen Oximoron: Sie sind inhaltlich gleich, aber eben nicht identisch. Identisch wären sie, wenn sie beide dasselbe Objekt wären.

Der standard-HashCode wird vermutlich(!) auf Basis von sowas wie einer Speicheradresse berechnet - das spielt keine so große Rolle, es wird leglich versucht (so weit das möglich ist) für unterschiedliche Objekte auch unterschiedliche hashCodes zurückzuliefern.

Wenn man eine eigene Klasse erstellt, und erreichen will, dass zwei "inhaltlich gleiche" Objekte auch den gleichen hashCode haben, muss man die hashCode-Methode überschreiben. In diesem Beispiel könnte das z.B. sowas sein wie
Code:
public int hashCode()
{
    return 31 * titel.hashCode() + laenge;
}
oder so. Fast noch wichtiger ist aber, dass man auch die "equals"-Methode überschreibt - und zwar so, dass sie zur hashCode-Methode passt: Wenn zwei Objekte "equals" sind, müssen auch die hashCodes gleich sein.

Eclipse (und andere IDEs vermulich auch) bietet eine Funktion, um diese Methoden automatisch generieren zu lassen (Source->Generate equals and hashCode)
 
Servus Männer,

Danke für eure Rückmeldungen.

Ich weiß nun, wie HashCode und equal zueinander stehen müssen, aber bin immernoch nicht ganz sicher, wie das in der Praxis aussieht. Daher folgende Fragen:

1. Obwohl meine Objekte o1 und o2 inhaltlich äquivalent sind (nennen wir es doch so Marco 😉) geben sie unterschiedliche Hashcodes zurück. 2 Objekte auf dem Heap haben also wirklich niemals denselben Hashcode, wenn die Methode Hashcode von Object nicht überschrieben wird.
Wo wir auch schon beim Punkt meiner Frage sind: Wenn ich Hashcode in meinem Fall oben mit titel.hashCode() überschreibe, dann müsste ich doch für o1 und o2 dasselbe Ergebnis erhalten, oder? Die Titel sind ja gleich?! Irgendwie funzt das aber nicht, da sich der HashCode überhaupt nicht verändert?!

2. Es heißt ja:
"Wenn equals() in einer Klasse nicht überschrieben wird, können 2 verschiedene Objekte niemals als gleich angesehen werden, da Referenzen auf zwei verschiedene Objekte immer ein unterschiedliches Bitmuster enthalten werden."

Ist das Bitmuster auch unterschiedlich, wenn alle Instanzvariablen mit den gleichen Werten belegt sind (wie in meinem Fall oben)? Für mein Verständnis ist das Bitmuster eine Anordnung von 0er und 1er Bits. Bei 2 Objektem mit äquivalentem Inhalt müsste diese Abfolge doch auch identisch sein, oder nicht?!


Besten Dank euch!

Seb
 
Es ist eigentlich ganz einfach. Die Standardimplementierung für equals und hashCode ist, dass jedes Objekt nur zu sich selbst "gleich" ist. Der Inhalt der Objekte ist dabei völlig irrelevant:
Java:
class Person {
   public String name; 
   public Person(String name) { this.name = name; }
}

Person a1 = new Person("Alfred");
Person a2 = a1;
Person a3 = new Person("Alfred");
System.out.println(a1.equals(a2)); // true
System.out.println(a1.equals(a3)); // false
Hier zeigt a1 und a2 auf eine andere Speicherstelle als a3, also auf ein anderes Objekt im Speicher. Das kann man auch ganz einfach erkennen, indem man das Attribut name ändert:
Java:
a1.name = "Berthold";
System.out.println(a2.name); // Berthold
System.out.println(a3.name); // Alfred

Will man ein anderes Verhalten (z.B. dass in unserem Beispiel a1 und a3 auch als gleich angesehen werden), muss man es selber implementieren. Dabei sollte man auf die Einhaltung der "Regeln" für equals und hashCode achten, also equals == true --> gleiche Hashcodes.
 
Die Titel sind ja gleich?! Irgendwie funzt das aber nicht, da sich der HashCode überhaupt nicht verändert?!

Meine Glaskugel sagt, dass die IDE meckern würde, wenn du ein [c]@Override[/i] vor die hashCode-Methode schreiben würdest 😉 Mal im Ernst: Poste sie vielleicht mal.


Ist das Bitmuster auch unterschiedlich, wenn alle Instanzvariablen mit den gleichen Werten belegt sind (wie in meinem Fall oben)? Für mein Verständnis ist das Bitmuster eine Anordnung von 0er und 1er Bits. Bei 2 Objektem mit äquivalentem Inhalt müsste diese Abfolge doch auch identisch sein, oder nicht?!

Naja, das mit dem Bitmuster... passt glaubich nicht so. Ein VERSUCH einer Erklärung: Wenn man zwei Objekte anlegt, die den gleichen Inhalt haben, dann steht für das erste Objekt im Speicher irgendwo sowas wie
3 1.2 "Hallo"
(und so gesehen könnte man das als "Bitmuster im Speicher" interpretieren, wenn man will). An einer ANDEREN Stelle im Speicher steht AUCH
3 1.2 "Hallo"
So gesehen wären die "Bitmuster", die die Objekte beschreiben, dann gleich. Beim "normalen" equals-Vergleich werden aber (sozusagen!!!) nicht diese "Bitmuster" verglichen, sondern die STELLEN an denen diese Bitmuster im Speicher stehen. Und die sind unterschiedlich.
(Diese Aussagen sind eigentlich so unpräzise, dass man sie schon "falsch" nennen könnte, aber ich hoffe, sie machen deutlicher, was dort eigentlich abläuft)
 
die hashcode methode beachtet deine instanzvariablen überhaupt nicht, da sie zu java.lang.Object gehört und stets einen hashcode für ein java.lang.Object berechnet, selbiges gilt für equals... hier wird ohne überschreiben auch nur ein java.lang.Object verglichen, egal was der typ eigentlich ist.

will man also equals und hashcode vernünftig verwenden, so muss man die methoden überschreiben, was auch für z.B. entity klassen immer empfehlenswert ist.

das thema eindeutige hashcodes erzeugen ist allerdings eine kleine wissenschaft für sich und vielleicht auch eher was für mathematiker.
 
Danke für die Erklärungen!

Wie müsste ich die Methode hashCode() denn überschreiben, wenn in meinem Beispiel o1 und o2 den gleichen Wert zurück liefern sollen? (Die Methode equals() ist mir erstmal egal).

Das hier funzt nicht:

Java:
		public int hashCode() {
			return titel.hashCode();
		}

Muss ich da irgendwelchen Konventionen bei der Überschreibung folgen?

Besten Dank!

GLG, Seb
 
Das sollte eigentlich gehen (allerdings wird dann laenge ignoriert). Hast du die equals-Methode auch entsprechend überschrieben?
 
Ich kann mir doch von 2 Objekten den gleichen Hashcode zurückgeben lassen, auch wenn diese nicht ganz equal sind, oder? Von daher muss ich in diesem Fall equals doch gar nicht überschreiben, oder sehe ich das falsch?
Ich will die Objekte ja keinem HashSet hinzufügen oder so. Dann sollte equals doch egal sein?!
 
So gesehen stimmt das: Man kann auch
Code:
public int hashCode() 
{ 
    return 0; 
}
schreiben, und braucht sich um equals so gesehen keine Gedanken zu machen. Allerdings wäre das [c]return titel.hashCode()[/c] schon besser 😉 Und OB man equals nicht doch überschreiben sollte, sollte man sich auch überlegen...
 
Wenn es gehen sollte, dann bräucht ich die überschriebene Hashcode Methode doch nur in meinem obigen Beispiel einfügen und sollte gleiche Hashcodes für o1 und o2 erhalten, richtig?

Die Objekte liefern aber unterschiedliche Ergebnisse zurück?!

Zumindest bei mir :autsch:.
 
@Marco13:
Ein Oximoron ist es vielleicht – keine Ahnung, was das ist –, ein Oxymoron ist es aber nicht. Es klingt zwar „toll“, ist aber hier falsch. Dafür ist widersprüchliches Oxymoron pleonastisch. 😉

@EG2009:
> Ich kann mir doch von 2 Objekten den gleichen Hashcode zurückgeben
> lassen, auch wenn diese nicht ganz equal sind, oder?

Ja, das kannst Du. Umgekehrt hingegen verstieße man gegen die definierte Voraussetzung, daß zwei Objekte, die im Sinne der equals-Methode gleich sind, den gleichen Streuwert zurückgeben müssen.

> Von daher muss ich in diesem Fall equals doch gar nicht
> überschreiben, oder sehe ich das falsch?

Das siehst Du richtig.

Für weitere Informationen zu diesem Thema kann ich Dir einen Blick auf diese Seite empfehlen:
AngelikaLanger.com - Implementing the hashCode() Method - Angelika Langer Training/Consulting
Wie hashCode() und equals() miteinander zusammenhängen, wird im Kapitel Der sogenannte hashCode-Contract erklärt.

> Wenn es gehen sollte, dann bräucht ich die überschriebene Hashcode
> Methode doch nur in meinem obigen Beispiel einfügen und sollte
> gleiche Hashcodes für o1 und o2 erhalten, richtig?

Beide sollten 0 zurückgeben. Wenn nicht, hast Du etwas falsch gemacht.
 
Von Tippfehlern mal abgesehen finde ich widersprüchliche Oxymora, wahre Tautologien und wahre Oxymora und widersprüchliche Tautologien eben toll 😀 Eigentlich wollte ich nur andeuten, dass sich Identität nicht auf den Inhalt beziehen kann. 😳
 

Zurück
Oben