Java hashCode equals Problem

IsNoGod

Aktives Mitglied
Thread Starter
Dabei seit
06.01.2006
Beiträge
260
Reaktionspunkte
5
Ich habe das Problem, dass die Equals-Methode sagt, beide Instanzen sein "gleich", ich aber nicht den selben hashCode bekomme.
Die beiden Methoden sind von Eclipse auto-erzeugt. Die hashCode-Methode meiner Klasse ruft ja hashCode von einem Set auf.

Liegt der Fehler jetzt darin, dass ich die hashCode Methode jetzt wirklich selber einbauen muss und nicht die einer anderen Klasse nehmen darf oder doch wo anders?


in der Klasse
Code:
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		
		result = prime * result + ((points == null) ? 0 : points.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ProxyTreeSet other = (ProxyTreeSet) obj;
		if (points == null) {
			if (other.points != null)
				return false;
		} else if (!points.equals(other.points))
			return false;
		return true;
	}

in der Testklasse
Code:
	@Test
	public void testEquals()
	{
		System.out.println(">>> testEquals()");
		points.add(new Point(0.1, 0.12));
		points.add(new Point(0.2, 0.22));
		points.add(new Point(0.3, 0.32));

		ProxyTreeSetInterface tmpPoints = Factory.INSTANCE.createProxyTreeSetInterface();
		tmpPoints.add(new Point(0.1, 0.12));
		tmpPoints.add(new Point(0.2, 0.22));
		tmpPoints.add(new Point(0.3, 0.32));
		
		assertTrue(points.equals(tmpPoints));
		System.out.println("\tpoints and tmpPoints are equal");
		System.out.println("\ttmpPoints:");
		System.out.println(getPointsStringRepresentation(points));
		System.out.println("\tpoints:");
		System.out.println(getPointsStringRepresentation(points));
	}
	
	@Test
	public void testHashCode()
	{
		System.out.println(">>> testHashCode()");
		points.add(new Point(0.1, 0.12));
		points.add(new Point(0.2, 0.22));
		points.add(new Point(0.3, 0.32));

		ProxyTreeSetInterface tmpPoints = Factory.INSTANCE.createProxyTreeSetInterface();
		tmpPoints.add(new Point(0.1, 0.12));
		tmpPoints.add(new Point(0.2, 0.22));
		tmpPoints.add(new Point(0.3, 0.32));
		
		assertEquals(tmpPoints.hashCode(), points.hashCode());
		System.out.println("\tpoints and tmpPoints have the same hashCode");
		System.out.println("\tpoints hashCode: " + points.hashCode());
		System.out.println("\ttmpPoints hashCode: " + tmpPoints.hashCode());
	}
 
Hm, ich weiß jetzt nicht, in welcher Relation die beiden Klassen stehen, aber solltest du nicht, wenn du die Gleichheit über den Hash-Code wissen willst, diesen auch in der equals()-Methode prüfen?
 
Das erste ist die Klasse selber, das zweite ist die Testklasse (JUnit) zu der Klasse.

ich versteh nicht, warum ich zwei unterschiedliche hashcode bekomme?
 
Zuletzt bearbeitet:
Schau mal in der Methode, wo Du die Gleichheit der Hashcodes testest, vorher, ob die Objekte tatsächlich gleich sind!
Also füge

assertTrue(points.equals(tmpPoints));
System.out.println("\tpoints and tmpPoints are equal");

in der testHashCode-Methode ein.

Es könnte ja sein, daß die Variablen points und tmpPoints verschieden belegt sind zum Zeitpunkt des Methodenaufrufs.
 
geänderte Testklasse
Code:
	@Test
	public void testHashCode()
	{
		System.out.println(">>> testHashCode()");
		points.add(new Point(0.1, 0.12));
		points.add(new Point(0.2, 0.22));
		points.add(new Point(0.3, 0.32));

		ProxyTreeSetInterface tmpPoints = Factory.INSTANCE.createProxyTreeSetInterface();
		tmpPoints.add(new Point(0.1, 0.12));
		tmpPoints.add(new Point(0.2, 0.22));
		tmpPoints.add(new Point(0.3, 0.32));
		
		[B]System.out.println("points :");
		System.out.println(getPointsStringRepresentation(points));
		System.out.println("tmpPoints :");
		System.out.println(getPointsStringRepresentation(tmpPoints));
		System.out.println("Equal? " + points.equals(tmpPoints));
		System.out.println("points.hashCode() : " + points.hashCode());
		System.out.println("tmpPoints.hashCode() : " + tmpPoints.hashCode());[/B]
		
		[B]assertTrue(points.equals(tmpPoints)); [/B]
		assertEquals(tmpPoints.hashCode(), points.hashCode());
		System.out.println("\tpoints and tmpPoints have the same hashCode");
		System.out.println("\tpoints hashCode: " + points.hashCode());
		System.out.println("\ttmpPoints hashCode: " + tmpPoints.hashCode());
	}


Konsole
Code:
>>> testHashCode()
points :
	x: 0.1, y: 0.12
	x: 0.2, y: 0.22
	x: 0.3, y: 0.32

tmpPoints :
	x: 0.1, y: 0.12
	x: 0.2, y: 0.22
	x: 0.3, y: 0.32

Equal? true
points.hashCode() : 28499600
tmpPoints.hashCode() : 56024807
 
http://java.sun.com/j2se/1.4.2/docs/api/java/util/Set.html#hashCode()

Der Hash-Code eines Sets ist die Summe der Hash-Codes seiner enthaltenen Objekte. Ist eine Objektreferenz gleich null, ist der Hash-Wert 0.

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#hashCode()

Der Hashcode jeder Objektinstanz ist unterscheidbar. Sie wird aus der Adresse des Objekts erzeugt.

und:

Deine equals-Methode fasst einfach nirgends und fällt auf den default am Ende zurück!

Wenn du die Gleichheit über die Hash-Codes sicherstellen möchtest, nutze doch bitte auch diese. Wobei dann auch die Definition von == auf Objekten greift, welches die Referenzen (also Adressen) vergleicht und damit einem Vergleich der Hashes von Objekten die das Standard-Verhalten bon Object nicht überschreiben gleich kommt.

http://leepoint.net/notes-java/data/expressions/22compareobjects.html
 
Hey _ebm_,
schau mal:
Code:
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ProxyTreeSet other = (ProxyTreeSet) obj;
		if (points == null) {
			if (other.points != null)
				return false;
		} else if [B](points.equals(other.points))[/B]
			return [B]true[/B];
		return [B]false[/B];
	}
habs fett gemacht, ich hoffe man sieht das. Ich benutz da die .equals() von einem Set (bzw. TreeSet) und das liefert mir zurück, dass beide gleich sind. Es stimmt also nicht, dass die equals-Methode nirgendwo fasst stimmt also nicht.

Du hast mich allerdings auf was anderes gebracht, und zwar:

Code:
	@Test
	public void testHashCode()
	{
		Point pointA = new Point(0.1, 0.12);
		Point pointB = new Point(0.2, 0.22);
		Point pointC = new Point(0.3, 0.32);
		
		System.out.println(">>> testHashCode()");
		points.add(pointA);
		points.add(pointB);
		points.add(pointC);

		ProxyTreeSetInterface tmpPoints = Factory.INSTANCE.createProxyTreeSetInterface();
		tmpPoints.add(pointA);
		tmpPoints.add(pointB);
		tmpPoints.add(pointC);

		assertEquals(tmpPoints.hashCode(), points.hashCode());
		System.out.println("\tpoints and tmpPoints have the same hashCode");
		System.out.println("\tpoints hashCode: " + points.hashCode());
		System.out.println("\ttmpPoints hashCode: " + tmpPoints.hashCode());
	}
und damit läuft der Test auch durch
woher soll das Set hashCode() auch wissen, wann Instanzen meiner Klasse gleich sind (was es per Definition ja wissen muss)?

Ich vermute jetzt einfach mal, dass .equals() von Set (bzw. TreeSet) .compareTo() meiner Point-Klasse (nicht hier dargestellt) verwendet um zu vergleichen und deswegen true zurück liefert/liefern kann.

Ich werd jetzt mal schauen, ob .equals() mein .compareTo() benutzt und dann die .hashCode() anpassen und hoffen, dass ich da nicht gegen irgendwas verstoße.
 
Code:
	@Override
	public boolean equals(Object obj) {

                ...

		} else if ([b]![/b]points.equals(other.points))
			return [b]false;[/b]
		return [b]true;[/b]
	}

Code:
	@Override
	public boolean equals(Object obj) {
		} else if [B](points.equals(other.points))[/B]
			return [B]true[/B];
		return [B]false[/B];
	}
habs fett gemacht, ich hoffe man sieht das. Ich benutz da die .equals() von einem Set (bzw. TreeSet) und das liefert mir zurück, dass beide gleich sind. Es stimmt also nicht, dass die equals-Methode nirgendwo fasst stimmt also nicht.

Hm, da hast du die Logik umgedreht, dann fasst die Logik! ;)

Du hast mich allerdings auf was anderes gebracht, und zwar:

Gern ;)
 
ich will mich um so einen Kleinkramm sicher nicht streiten, aber das Ergebnis von beidem ist das selbe. ;)

Zur endgültigen Lösung:

Ich rufe in meinem Proxy zum TreeSet ja die .hashCode() vom Set auf, die wiederrum die .hashCode() auf jedem einzelnen Element im Set aufruft. Problem: Da das eine von mir geschriebene Klasse war, hatte ich natürlich nicht daran gedacht eine .equals() und eine .hashCode() zu implementieren. Hab ich jetzt getan, es geht, die grauen Haare bleiben, aber zumindest kann ich heute Nacht schlafen.

Danke für die Hilfe!
 
  • Gefällt mir
Reaktionen: Marduk
das sind diese Fehler wo man sich hinterher ärgert warum man nicht vorher drüber nachgedacht hat :p

Ein cooles Buch das genau all solche "Kleinigkeiten" angeht und das ich in dem Kontext nur empfehlen kann ist dies hier:

[ISBN]0321356683[/ISBN]

da wird all sowas sauber erklärt und durchgegangen und noch sehr viel mehr! Ein lesens- und lernenswertes Buch :)
 
Zurück
Oben Unten