{"id":1316,"date":"2012-05-12T14:11:01","date_gmt":"2012-05-12T12:11:01","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=1316"},"modified":"2012-05-12T14:11:02","modified_gmt":"2012-05-12T12:11:02","slug":"inselraus-identityhashcode-ist-fr-eine-system-eindeutige-objekt-ids-nicht-geeignet","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2012\/05\/inselraus-identityhashcode-ist-fr-eine-system-eindeutige-objekt-ids-nicht-geeignet\/","title":{"rendered":"Inselraus: identityHashCode() ist f&uuml;r eine System-eindeutige Objekt-IDs nicht geeignet"},"content":{"rendered":"<p>Stellen wir uns vor, wir h\u00e4tten eine Objekthierarchie im Speicher, die zum Beispiel jeder Socke einen Besitzer zuspricht. Wenn wir im Speicher Assoziationen abbilden, dann sollen diese Verweise auch noch nach dem Tod des Programms \u00fcberleben. Eine L\u00f6sung ist die Serialisierung, eine andere eine Objekt-Datenbank oder aber auch eine XML-Datei. Doch \u00fcberlegen wir selbst, wo bei der Abbildung auf eine Datenbank oder eine Datei das Problem besteht. Zun\u00e4chst stehen ganz unterschiedliche Objekte mit ihren Eigenschaften im Speicher. Das Speichern der Zust\u00e4nde ist kein Problem, denn nur die Attribute m\u00fcssten abgespeichert werden. Doch wenn ein Objekt auf ein anderes verweist, muss dieser Verweis gesichert werden. Aber in Java ist ein Verweis durch eine Referenz gegeben, und was sollte es da zu speichern geben? Eine L\u00f6sung f\u00fcr das Problem ist, jedem Objekt im Speicher einen Z\u00e4hler zu geben und beim Speichen etwa zu sagen: \u00bbDer Besitzer 2 kennt Socke 5\u00ab.<\/p>\n<p>Der Identifizierer f\u00fcr die Objekte muss eindeutig sein, und wir k\u00f6nnen \u00fcberlegen, System.identityHashCode() zu nutzen. In der Implementierung der virtuellen Maschine von Oracle geht in den Wert von identityHashCode() die Information \u00fcber den wahren Ort des Objekts im Speicher ein. Bei einer 64-Bit-Implementierung w\u00fcrden auch 32 Bit abgeschnitten, und die Eindeutigkeit w\u00e4re somit automatisch nicht mehr gew\u00e4hrleistet. Ein weiteres Problem besteht darin, dass zwar die Implementierung von Oracles identityHashCode() auf die eindeutige Objektspeicheradresse abbildet, aber dass das nicht jeder Hersteller so machen muss. Damit ist identityHashCode() nicht \u00fcberall gesichert unterschiedlich. Zudem ist es prinzipiell denkbar, dass die Speicherverwaltung die Objekte verschiebt. Was sollte identityHashCode() dann machen? Wenn die neue Speicheradresse dahinter steckt, w\u00fcrde sich der Hashcode \u00e4ndern, und das darf nicht sein. Es k\u00e4me ebenfalls zu einem Problem, wenn mehr als Integer.MAX_INTEGER viele Objekte im Speicher st\u00fcnden. (Doch wenn wir uns die gro\u00dfe Zahl 2^32 = 4.294.967.296 vor Augen halten, dann es ist unwahrscheinlich, dass sich mehr als 4 Milliarden Objekte im Speicher tummeln. Zudem br\u00e4uchten wir 4 Gigabyte Speicher, wenn jedes Objekt auch nur 1 Byte kosten w\u00fcrde.)<\/p>\n<p>Es ist gar nicht so schwierig, zwei unterschiedliche Objekte mit gleichen identityHashCode()-Resultat zu bekommen. Wir erzeugen ein paar String-Objekte und testen, jedes mit jedem, ob identityHashCode() den gleichen Wert ergibt:<\/p>\n<p>String[] strings = new String[5000];   <br \/>for ( int i = 0; i &lt; strings.length; i++ )    <br \/>&#160; strings[i] = Integer.toString( i );    <br \/>int cnt = 0;    <br \/>for ( int i = 0; i &lt; strings.length; i++ ) {    <br \/>&#160; for ( int j = i + 1; j &lt; strings.length; j++ ) {    <br \/>&#160;&#160;&#160; int id1 = System.identityHashCode( strings[i] );    <br \/>&#160;&#160;&#160; int id2 = System.identityHashCode( strings[j] );    <br \/>&#160;&#160;&#160; if ( id1 == id2 ) {    <br \/>&#160;&#160;&#160;&#160;&#160; out.println( &quot;Zwei Objekte mit identityHashCode() = &quot; + id1 );    <br \/>&#160;&#160;&#160;&#160;&#160; out.println( &quot; Objekt 1: \\&quot;&quot; + strings[i] + &quot;\\&quot;&quot; );    <br \/>&#160;&#160;&#160;&#160;&#160; out.println( &quot; Objekt 2: \\&quot;&quot; + strings[j] + &quot;\\&quot;&quot; );    <br \/>&#160;&#160;&#160;&#160;&#160; out.println( &quot; Object1.hashCode(): &quot; + strings[i].hashCode() );    <br \/>&#160;&#160;&#160;&#160;&#160; out.println( &quot; Object2.hashCode(): &quot; + strings[j].hashCode() );    <br \/>&#160;&#160;&#160;&#160;&#160; out.println( &quot; Object1.equals(Object2): &quot; + strings[i].equals( strings[j] ) );    <br \/>&#160;&#160;&#160;&#160;&#160; cnt++;    <br \/>&#160;&#160;&#160; }    <br \/>&#160; }    <br \/>}    <br \/>System.out.println( cnt + &quot; Objekte mit gleichem identityHashCode() gefunden.&quot; );<\/p>\n<p>Ein Durchlauf bringt schnell Ergebnisse wie:<\/p>\n<p>Zwei Objekte mit identityHashCode() = 9578500   <br \/> Objekt 1: &quot;541&quot;    <br \/> Objekt 2: &quot;2066&quot;    <br \/> Object1.hashCode(): 52594    <br \/> Object2.hashCode(): 1537406    <br \/> Object1.equals(Object2): false    <br \/>Zwei Objekte mit identityHashCode() = 14850080    <br \/> Objekt 1: &quot;2085&quot;    <br \/> Objekt 2: &quot;2365&quot;    <br \/> Object1.hashCode(): 1537467    <br \/> Object2.hashCode(): 1540288    <br \/> Object1.equals(Object2): false    <br \/>2 Objekte mit gleichem identityHashCode() gefunden.<\/p>\n<p>Das Ergebnis ist also, dass identityHashCode() nicht sicher bei der Vergabe von Identifizierern ist. Um wirklich allen Problemen aus dem Weg zu gehen, ist ein Z\u00e4hlerobjekt oder eine ID \u00fcber zum Beispiel die Klasse java.util.UUID n\u00f6tig.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Stellen wir uns vor, wir h\u00e4tten eine Objekthierarchie im Speicher, die zum Beispiel jeder Socke einen Besitzer zuspricht. Wenn wir im Speicher Assoziationen abbilden, dann sollen diese Verweise auch noch nach dem Tod des Programms \u00fcberleben. Eine L\u00f6sung ist die Serialisierung, eine andere eine Objekt-Datenbank oder aber auch eine XML-Datei. Doch \u00fcberlegen wir selbst, wo [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[11],"tags":[],"class_list":["post-1316","post","type-post","status-publish","format-standard","hentry","category-insel"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1316","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=1316"}],"version-history":[{"count":1,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1316\/revisions"}],"predecessor-version":[{"id":1317,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1316\/revisions\/1317"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=1316"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=1316"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=1316"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}