2010-03-11 8 views
44

Welcher Wert ist hashCode() Methode wird in Java zurückgegeben?Wie wird hashCode() in Java berechnet

Ich lese, dass es eine Speicherreferenz eines Objekts ist ... Wenn ich Hash-Wert für new Integer(1) drucke, ist es 1; für String("a") ist 97.

Ich bin verwirrt: ist es ASCII oder welche Art von Wert ist?

Antwort

40

Ein Hashcode ist ein ganzzahliger Wert, der den Zustand des Objekts angibt, auf dem er aufgerufen wurde. Deshalb gibt ein Integer, der auf 1 gesetzt ist, einen Hashcode von "1" zurück, weil ein Integer's Hashcode und sein Wert dasselbe sind. Der Hashcode eines Charakters entspricht seinem ASCII-Zeichencode. Wenn Sie einen benutzerdefinierten Typ schreiben, müssen Sie eine gute hashCode Implementierung erstellen, die den Status der aktuellen Instanz am besten darstellt.

42

Der von hashCode() zurückgegebene Wert ist keinesfalls garantiert die Speicheradresse des Objekts. Ich bin mir nicht sicher über die Implementierung in der Object Klasse, aber bedenken Sie, dass die meisten Klassen hashCode() überschreiben werden, so dass zwei Instanzen, die semantisch äquivalent sind (aber nicht dieselbe Instanz sind), auf denselben Wert hashen. Dies ist besonders wichtig, wenn die Klassen in einer anderen Datenstruktur verwendet werden können, z. B. Set, die darauf beruht, dass mit equals übereinstimmt.

Es gibt keine hashCode(), die eine Instanz eines Objekts eindeutig identifiziert, egal was passiert. Wenn Sie einen Hashcode verwenden möchten, der auf dem zugrunde liegenden Zeiger basiert (z. B. in der Sun-Implementierung), verwenden Sie . Dies wird an die Standardmethode hashCode delegiert, unabhängig davon, ob sie überschrieben wurde.

Trotzdem kann sogar System.identityHashCode() den gleichen Hash für mehrere Objekte zurückgeben. Sehen Sie die Kommentare für eine Erklärung, aber hier ist ein Beispielprogramm, das kontinuierlich Objekte erzeugt, bis es zwei mit dem gleichen System.identityHashCode() findet. Wenn ich es ausführe, findet es schnell zwei System.identityHashCode() s, die übereinstimmen, im Durchschnitt nach dem Hinzufügen von etwa 86.000 langen Wrapper-Objekten (und Integer-Wrapper für den Schlüssel) zu einer Karte.

public static void main(String[] args) { 
    Map<Integer,Long> map = new HashMap<>(); 
    Random generator = new Random(); 
    Collection<Integer> counts = new LinkedList<>(); 

    Long object = generator.nextLong(); 
    // We use the identityHashCode as the key into the map 
    // This makes it easier to check if any other objects 
    // have the same key. 
    int hash = System.identityHashCode(object); 
    while (!map.containsKey(hash)) { 
     map.put(hash, object); 
     object = generator.nextLong(); 
     hash = System.identityHashCode(object); 
    } 
    System.out.println("Identical maps for size: " + map.size()); 
    System.out.println("First object value: " + object); 
    System.out.println("Second object value: " + map.get(hash)); 
    System.out.println("First object identityHash: " + System.identityHashCode(object)); 
    System.out.println("Second object identityHash: " + System.identityHashCode(map.get(hash))); 
} 

Beispiel Ausgang:

Identical maps for size: 105822 
First object value: 7446391633043190962 
Second object value: -8143651927768852586 
First object identityHash: 2134400190 
Second object identityHash: 2134400190 
+2

einen besonderen Grund dies wurde modded nach unten? Wenn etwas hier falsch ist, würde ich eine Korrektur lieben, aber ohne Erklärung zu verwerfen, bringt nichts zur Diskussion. – danben

+6

Vor ein paar Jahren erklärte Ted Neward in http://blogs.tedneward.com/2008/07/16/ObjecthashCode+Implementation.aspx wie die OpenJDK Object.hashCode() implementiert. Der OpenJDK leitet den Hash-Code von der Objektadresse ab, speichert diesen Wert jedoch im Cache und gibt ihn an nachfolgende Aufrufer zurück, falls sich das Objekt im Speicher bewegt und seine Adresse ändert. Nachdem ich kurz den neuesten Code gelesen habe, habe ich festgestellt, dass sich die Implementierung nicht geändert hat, seit Neward seinen Artikel geschrieben hat. –

+0

Das scheint meine Antwort zu unterstützen. – danben

4

Die hashCode() Verfahren wird häufig zur Identifizierung eines Objekts verwendet. Ich denke, die Object Implementierung gibt den Zeiger (kein echter Zeiger, sondern eine eindeutige ID oder etwas ähnliches) des Objekts zurück. Aber die meisten Klassen überschreiben die Methode. Wie die String Klasse. Zwei String-Objekte haben nicht den gleichen Zeiger, aber sie sind gleich:

new String("a").hashCode() == new String("a").hashCode() 

Ich denke, die häufigste Verwendung für hashCode() ist in Hashtable, HashSet, etc ..

Java API Object hashCode()

Edit: (aufgrund eines kürzlichen Downvotes und basierend auf einem Artikel, den ich über JVM-Parameter gelesen habe)

Mit dem JVM-Parameter -XX:hashCode können Sie ändern, wie der HashCode berechnet wird (siehe Issue 222 des Java-Spezialisten-Newsletters).

HashCode == 0: Gibt einfach Zufallszahlen ohne Beziehung zurück, wo im Speicher das Objekt gefunden wird. Soweit ich das feststellen kann, ist das globale Read-Write des Seeds nicht optimal für Systeme mit vielen Prozessoren.

HashCode == 1: Zählt die Hash-Code-Werte, nicht sicher bei welchem ​​Wert sie beginnen, aber es scheint ziemlich hoch.

HashCode == 2: Gibt immer den identischen Identitätshashcode von 1 zurück. Dies kann zum Testen von Code verwendet werden, der auf Objektidentität angewiesen ist. Der Grund, warum JavaChampionTest Kirk's URL im obigen Beispiel zurückgegeben hat, ist, dass alle Objekte denselben Hash-Code zurückgegeben haben.

HashCode == 3: Zählt die Hash-Code-Werte, beginnend bei Null. Es scheint threadsicher nicht zu sein, so dass mehrere Threads Objekte mit demselben Hash-Code generieren können.

HashCode == 4: Dies scheint eine Beziehung zum Speicherort zu haben, bei dem das Objekt erstellt wurde.

HashCode> = 5: Dies ist der Standardalgorithmus für Java 8 und hat einen pro-Thread-Seed. Es verwendet Marsaglias Xor-Shift-Schema, um Pseudozufallszahlen zu erzeugen.

+0

Wenn ich den Code richtig lese, ist die Standardstrategie in Java 8 & 9 5. –

1

Object.hashCode(), wenn der Speicher korrekt (überprüfen Sie die JavaDoc für java.lang.Object) dient, ist abhängig von der Implementierung, und wird sich ändern, je nach Objekt (der Sun JVM leitet den Wert aus dem Wert der Referenz auf das Objekt).

Beachten Sie, dass Sie, wenn Sie ein nichttriviales Objekt implementieren und diese korrekt in einer HashMap oder HashSet speichern möchten, hashCode() und equals() überschreiben müssen. hashCode() kann tun, was immer Sie wollen (es ist völlig legal, aber suboptimal, wenn es 1. zurückgibt), aber wenn Ihre equals() -Methode true zurückgibt, ist der von hashCode() für beide Objekte zurückgegebene Wert gleich.

Verwirrung und mangelndes Verständnis von hashCode() und equals() ist eine große Fehlerquelle. Stellen Sie sicher, dass Sie sich gründlich mit den JavaDocs für Object.hashCode() und Object.equals() vertraut machen, und ich garantiere, dass sich die aufgewendete Zeit amortisiert.

5

ich gelesen, dass es sich um eine Speicherreferenz eines Objekts ist ..

Nr Object.hashCode() verwendet vor einer Speicheradresse über 14 Jahre zurück. Nicht seit.

welche Art von Wert ist

Was es ist, hängt ganz von welcher Klasse Sie reden und ob es außer Kraft gesetzt `Object.hashCode().

+1

Dies liefert keine Antwort auf die Frage. Um einen Autor zu kritisieren oder um Klärung zu bitten, hinterlasse einen Kommentar unter seinem Beitrag. –

+0

@chsdk Müll. Es widerlegt eine Behauptung, die in der Frage gemacht wurde. "Nein" ist eine Antwort. NB: Ich bitte auch nicht um Aufklärung. – EJP

+0

Dies war ein automatischer Kommentar, nach einer Flagge als "keine Antwort", die vor Ihrer letzten Bearbeitung vor 2 Stunden unklar und unerklärlich war. –

19

Wenn Sie wissen möchten, wie sie implemented sind, empfehle ich Ihnen, die Quelle zu lesen. Wenn Sie eine IDE verwenden, können Sie einfach + auf eine Methode, an der Sie interessiert sind, zugreifen und sehen, wie eine Methode implementiert wird. Wenn Sie das nicht können, können Sie nach der Quelle googlen.

Zum Beispiel Integer.hashCode() implementiert als

public int hashCode() { 
     return value; 
    } 

und String.hashCode()

public int hashCode() { 
     int h = hash; 
     if (h == 0) { 
      int off = offset; 
      char val[] = value; 
      int len = count; 

      for (int i = 0; i < len; i++) { 
       h = 31*h + val[off++]; 
      } 
      hash = h; 
     } 
     return h; 
    } 
+0

Ich habe geplant genau so zu antworten; +1 – incarnate

+0

@Peter Lawrey und wie kann ich die Objekt hashCode-Implementierung –

+0

@naroji Es ist in der OpenJDK. Leider gibt es mehrere Strategien und es ist nicht klar, welche verwendet wird. –

-1
public static int murmur3_32(int paramInt1, char[] paramArrayOfChar, int paramInt2, int paramInt3) { 
/* 121 */  int i = paramInt1; 
/*  */  
/* 123 */  int j = paramInt2; 
/* 124 */  int k = paramInt3; 
/*  */  
/*  */  int m; 
/* 127 */  while (k >= 2) { 
/* 128 */  m = paramArrayOfChar[(j++)] & 0xFFFF | paramArrayOfChar[(j++)] << '\020'; 
/*  */  
/* 130 */  k -= 2; 
/*  */  
/* 132 */  m *= -862048943; 
/* 133 */  m = Integer.rotateLeft(m, 15); 
/* 134 */  m *= 461845907; 
/*  */  
/* 136 */  i ^= m; 
/* 137 */  i = Integer.rotateLeft(i, 13); 
/* 138 */  i = i * 5 + -430675100; 
/*  */  } 
/*  */  
/*  */ 
/*  */ 
/* 143 */  if (k > 0) { 
/* 144 */  m = paramArrayOfChar[j]; 
/*  */  
/* 146 */  m *= -862048943; 
/* 147 */  m = Integer.rotateLeft(m, 15); 
/* 148 */  m *= 461845907; 
/* 149 */  i ^= m; 
/*  */  } 
/*  */  
/*  */ 
/*  */ 
/* 154 */  i ^= paramInt3 * 2; 
/*  */  
/*  */ 
/* 157 */  i ^= i >>> 16; 
/* 158 */  i *= -2048144789; 
/* 159 */  i ^= i >>> 13; 
/* 160 */  i *= -1028477387; 
/* 161 */  i ^= i >>> 16; 
/*  */  
/* 163 */  return i; 
/*  */ } 

Wenn Sie wirklich neugierig dann durch diesen Code in Hashing.class gehen zu lernen;

Hier ersten param HASHING_SEED berechnet wird basierend auf folgenden Code

{ 
    long nanos = System.nanoTime(); 
    long now = System.currentTimeMillis(); 
    int SEED_MATERIAL[] = { 
      System.identityHashCode(String.class), 
      System.identityHashCode(System.class), 
      (int) (nanos >>> 32), 
      (int) nanos, 
      (int) (now >>> 32), 
      (int) now, 
      (int) (System.nanoTime() >>> 2) 
    }; 

    // Use murmur3 to scramble the seeding material. 
    // Inline implementation to avoid loading classes 
    int h1 = 0; 

    // body 
    for (int k1 : SEED_MATERIAL) { 
     k1 *= 0xcc9e2d51; 
     k1 = (k1 << 15) | (k1 >>> 17); 
     k1 *= 0x1b873593; 

     h1 ^= k1; 
     h1 = (h1 << 13) | (h1 >>> 19); 
     h1 = h1 * 5 + 0xe6546b64; 
    } 

    // tail (always empty, as body is always 32-bit chunks) 

    // finalization 

    h1 ^= SEED_MATERIAL.length * 4; 

    // finalization mix force all bits of a hash block to avalanche 
    h1 ^= h1 >>> 16; 
    h1 *= 0x85ebca6b; 
    h1 ^= h1 >>> 13; 
    h1 *= 0xc2b2ae35; 
    h1 ^= h1 >>> 16; 

    HASHING_SEED = h1; 
} 

der zweite param ist char Array von String, der dritte ist immer '0' und vierte ist char Arraylänge.

Und die obige Berechnung ist nur für String-Hash-Code.

Für alle Ganzzahlen ist der Hash-Code der Integer-Wert. Für char (bis zu zwei Buchstaben) wird es ASCII-Code sein.

+0

Ich weiß nicht, wo dieser Code herkommt, aber er hat nichts damit zu tun, wie Java '' String :: hashCode() 'berechnet wird. Oder irgendein anderer Hash-Code des Standard-Java-Typs ... AFAIK. Der * real * -Code für 'String :: hashCode()' ist hier: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/String .java # String.hashCode% 28% 29 –

0

So weit wie vernünftigerweise praktisch ist, gibt die Methode hashCode, die von der Klasse Object definiert wird, eindeutige ganze Zahlen für unterschiedliche Objekte zurück. (Dies wird in der Regel durch die Umwandlung der internen Adresse des Objekts in eine ganze Zahl implementiert, aber diese Implementierung Technik ist nicht von der Programmiersprache Java ™ erforderlich.)

https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--