Inkonsequenz beim Operator "=="

hansmueller

Mitglied
Hallo,

ich habe ein Verständnisproblem bei dem Operator "==".
Wenn ich es richtig verstanden habe, wird bei Objekten mit den Operator "==" nicht der Inhalt, sondern die Reverenz (also der Speicherort) verglichen.
Zwei verschieden Objekte sollten daher immer "false" liefern, auch wenn deren Inhalt gleich ist. ==> Tut es aber nicht immer

Folgendes Beispielprogramm

Code:
public class MainProg
{ 
    public static void main(String[] args)
    {
    	Integer x = 1;
    	Integer y = 1;
    	
    	Double e = 1.0;
    	Double r = 1.0;
    	
    	Long c = 1l;
    	Long v = 1l;
    	
    	Float u = 1.0f;
    	Float i = 1.0f;
    	
    	System.out.println(x == y);
    	System.out.println(x.equals(y));
    	System.out.println("------------");
    	System.out.println(e == r);
    	System.out.println(e.equals(r));
    	System.out.println("------------");
    	System.out.println(c == v);
    	System.out.println(c.equals(v));
    	System.out.println("------------");
    	System.out.println(u == i);
    	System.out.println(u.equals(i));
    }
}

liefert folgende Ausgabe:

Code:
true
true
------------
false
true
------------
true
true
------------
false
true

Müsste theoretisch nicht bei allen Vergleichen mit "==" ein "false" herauskommen.

Warum ist das nicht so?:confused:

MfG
hansmueller
 
Bei dieser Variante kommt immer das heraus, was du erwartest:

Java:
public static void main(String[] args) {
		Integer x = new Integer(1);
		Integer y = new Integer(1);

		Double e = 1.0;
		Double r = 1.0;

		Long c = new Long(11);
		Long v = new Long(11);

		Float u = 1.0f;
		Float i = 1.0f;

		System.out.println(x == y);
		System.out.println(x.equals(y));
		System.out.println("------------");
		System.out.println(e == r);
		System.out.println(e.equals(r));
		System.out.println("------------");
		System.out.println(c == v);
		System.out.println(c.equals(v));
		System.out.println("------------");
		System.out.println(u == i);
		System.out.println(u.equals(i));
	}

Bleibt zu klären wo der unterschied zwischen:
Java:
Integer x = 1;
und
Java:
Integer x = new Integer(1);
liegt.

Ich hätt eigentlich auch erwartet das in beiden Fällen eine eigene Instanz angelegt wird.
Außerdem warum verhält es sich bei Double und Float nicht so, sonder nur bei Integer und Long?

Fragen über Fragen, evtl. postet ja gleich noch jemand der mehr Ahnung hat als wir beiden.
 
Hab gerade mal noch ein wenig herumgetestet.
Wenn du eine Variable über
Code:
	Integer x = 1;
dann vergleicht der == Operator den Inhalt der Variable.
Wenn du über den über NEW instanzierst hast, vergleicht er die Instanzen.

Java:
package test;

public class GleichGleich {
	public static void main(String[] args) {

		Integer a = new Integer(1);
		Integer b = new Integer(1);
		
		Integer c = 1;
		Integer d = 1;
		
		System.out.println("a und b");
		vergleichen(a,b);
		System.out.println("c und d");
		vergleichen(c,d);
		
		
		System.out.println("a und c");
		vergleichen(a,c);
		System.out.println("b und d");
		vergleichen(b,d);
		
		
		System.out.println("c und a");
		vergleichen(c,a);
		System.out.println("d und b");
		vergleichen(d,b);
	}

	public static void vergleichen(Integer x, Integer y) {
		// x hochzählen
		x += 1;

		// Inhalte ausgeben
		System.out.println(x+" == "+y+" : "+(x==y));

		// x runterzählen
		x -= 1;

		// Inhalte ausgeben
		System.out.println(x+" == "+y+" : "+(x==y));
		System.out.println("");
	}

}

Ausgabe:
Code:
a und b
2 == 1 : false
1 == 1 : false

c und d
2 == 1 : false
1 == 1 : true

a und c
2 == 1 : false
1 == 1 : true

b und d
2 == 1 : false
1 == 1 : true

c und a
2 == 1 : false
1 == 1 : false

d und b
2 == 1 : false
1 == 1 : false
 
Zuletzt bearbeitet:
Schon sehr merkwürdig.

Bei Boolean hat man den gleichen Effekt.

Code:
public class MainProg
{ 
    public static void main(String[] args)
    {
    	Boolean j = true;
    	Boolean k = true;
    	
    	Boolean n = new Boolean(true);
    	Boolean m = new Boolean(true);
    	    	
    	System.out.println(j == k);
    	System.out.println(j.equals(k));
    	System.out.println("------------");
    	System.out.println(n == m);
    	System.out.println(n.equals(m));
    }
}
liefert
Code:
true
true
------------
false
true

Vielleicht liegt es daran, daß es sich eigendlich um elemtare Typen handelt, welche nur von einer Wrapperklasse umgeben sind. Elementare Typen sind keine Klassen bzw. Objekte im eigendlichen Sinn, sondern stehen außerhalb des objektorientierten Konzepts von Java. (Ich hoffe, daß stimmt so, wie ich es geschrieben habe.)

Dieses inkonsequente Verhalten wird dadurch aber auch nicht geklärt.

Es wäre wirklich interessant zu wissen, ob dieses Verhalten gewollt ist und eine Logik dahintersteckt, oder ob es sich einfach um einen Bug handelt?
 
Um noch eine Theorie in den Raum zu werfen:
Es gibt bei Strings einen Pool, in dem zuerst geschaut wird, ob bereits eine Instanze mit dem Wert existiert, und wenn ja, diese verwendet wird.
Falls man new verwendet wird das aber ignoriert, vielleicht ist es bei primitiven Typen genauso.
 
Die Erklärung hört sich gut an und könnte ein Schritt in die richtige Richtung sein.

Code:
public class MainProg
{ 
    public static void main(String[] args)
    {
    	String x = "Hallo";
    	String y = "Hallo";
    	
    	String e = new String("Hallo");
    	String r = new String("Hallo");
    	    	
    	System.out.println(x == y);
    	System.out.println(x.equals(y));
    	System.out.println("------------");
    	System.out.println(e == r);
    	System.out.println(e.equals(r));    	
    }
}
ergibt
Code:
true
true
------------
false
true

Es bleiben aber noch viele Fragen offen.

Warum gibt es für Gleitkomma-Typen keinen solchen Pool?
Und warum das unterschiedliche Verhalten bei den verschiedenen Arten der Instanziierung? (Einmal mit "new", einmal ohne.)
 
Moin,

also das ursprüngliche Problem hat mit den Pools so direkt erst mal gar nix zu tun. Grundlage für das Verhalten sind die Konventionen für das Autoboxing. Dabei werden laut Sprachspezifikation bestimmte Werte in identische Objekte umgewandelt. Garantiert wird das für die beiden boolean, alle byte, chars von \u0000 to \u007f sowie int und short von -128 bis 127. Für alle anderen Werte wird es nicht garantiert aber der Abschnitt in der Spec enthält noch die Anmerkung, dass im Idealfall alle Werte auf das gleiche Objekt zeigen sollten - was zumindest für long auch geht (aber auch dort nur im Bereich -128 bis 127).

Wenn man Vergleiche außerhalb der von der Spec garantierten Bereiche durchführt (z.B. Integer 512) kann true rauskommen, muss aber nicht (zumindest bei meiner Sun VM tut es das nicht). Im Endeffekt sollten Objekte immer mit equals() verglichen werden, wenn es um die Gleichheit geht, ohne irgendwelche Vermutungen anzustellen ob die Dinger nun identisch sind oder nicht.
For other values, this formulation disallows any assumptions about the identity of the boxed values on the programmer's part. This would allow (but not require) sharing of some or all of these references.

Und die Pools sind wieder eine andere Geschichte. Tatsächlich gibt es einen "klassenübergreifenden Pool" nur für Stringliterale (alles zwischen " ") - alle anderen Konstanten befinden sich in einen Pool pro Klasse. Über den Stringpool werden gleiche Stringliterale in den Klassen kanonisiert -> gleiche Werte werden durch ein identisches Objekt repräsentiert. Man kann auch zur Laufzeit erzeugte Strings mittels der intern() Methode kanonisieren. Diese schaut, ob es im Pool bereits ein gleicher (equals) String enthalten ist. Wenn ja liefert sie eine Referenz auf diesen String zurück. Wenn nein, wird ein gleichartiger String im Pool aufgenommen und eine Referenz auf diesen zurückgeliefert. Das Problem bei intern() ist, dass nicht spezifiziert ist, was passiert, wenn der String-Pool "voll läuft". Einige VM's steigen dann wohl mit einer OutOfMemory Exception aus. Die aktuellen Sun-VM's verwalten den Pool in einem speziellen Speicherbereich, der Permanent Generation auf dem auch eine Garbage Collection stattfindet - dadurch werden Einträge des Stringpool, die nirgends mehr verwendet werden entsprechend entfernt.

hth
THMD
 
Zurück