2010-03-11 27 views
44

¿Cuál es el valor del método hashCode() que está volviendo en java?¿Cómo se calcula hashCode() en Java

He leído que es una referencia de memoria de un objeto ... Cuando imprimo valor hash para new Integer(1) es 1; para String("a") es 97.

Estoy confundido: ¿es ASCII o qué tipo de valor es?

Respuesta

40

Un código hash es un valor entero que representa el estado del objeto al que se llamó. Es por eso que un Integer que se establece en 1 devolverá un código de hash de "1" porque un código de hash Integer's y su valor son la misma cosa. El código hash de un personaje es igual a su código de carácter ASCII. Si escribe un tipo personalizado, será responsable de crear una buena implementación de hashCode que mejor represente el estado de la instancia actual.

42

El valor devuelto por hashCode() no se garantiza en modo alguno como la dirección de memoria del objeto. No estoy seguro de la implementación en la clase Object, pero tenga en cuenta que la mayoría de las clases anularán hashCode() de modo que dos instancias que son semánticamente equivalentes (pero no son la misma instancia) hagan hash con el mismo valor. Esto es especialmente importante si las clases se pueden usar en otra estructura de datos, como Set, que se basa en hashCode y es coherente con equals.

No hay hashCode() que identifique de manera única una instancia de un objeto pase lo que pase. Si desea un código hash basado en el puntero subyacente (por ejemplo, en la implementación de Sun), use System.identityHashCode(); esto se delegará en el método hashCode predeterminado, independientemente de si se ha anulado.

Sin embargo, incluso System.identityHashCode() puede devolver el mismo hash para múltiples objetos. Consulte los comentarios para obtener una explicación, pero aquí hay un programa de ejemplo que genera continuamente objetos hasta que encuentra dos con el mismo System.identityHashCode(). Cuando lo ejecuto, encuentra rápidamente dos System.identityHashCode() s que coinciden, en promedio, después de agregar alrededor de 86,000 objetos de envoltura larga (y envoltorios enteros para la clave) a un mapa.

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))); 
} 

Ejemplo de salida:

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

alguna razón en particular esto se modded abajo? Si algo aquí es incorrecto, me encantaría una corrección, pero rechazar sin explicación no agrega nada a la discusión. – danben

+6

Hace unos años, Ted Neward explicó en http://blogs.tedneward.com/2008/07/16/ObjecthashCode+Implementation.aspx cómo el OpenJDK implementó Object.hashCode(). El OpenJDK deriva el código hash de la dirección del objeto, pero almacena en caché este valor y lo devuelve a los llamantes siguientes en caso de que el objeto se mueva en la memoria y su dirección cambie. Después de revisar brevemente el último código, descubrí que la implementación parece no haber cambiado desde que Neward escribió su artículo. –

+0

Esto parece apoyar mi respuesta. – danben

4

El método hashCode() se utiliza a menudo para identificar un objeto. Creo que la implementación Object devuelve el puntero (no un puntero real, sino una identificación única o algo así) del objeto. Pero la mayoría de las clases anulan el método. Como la clase String. Dos objetos String no tienen el mismo puntero, pero que son iguales:

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

creo que el uso más común para hashCode() está en Hashtable, HashSet, etc ..

Java API Object hashCode()

Editar: (debido a un voto a favor reciente y basado en un artículo que leí sobre los parámetros de JVM)

Con el parámetro JVM -XX:hashCode puede cambiar la forma en que se calcula hashCode (consulte el Issue 222 del Boletín de especialistas de Java).

HashCode == 0: Simplemente devuelve números aleatorios sin relación con el lugar donde se encuentra el objeto en memoria. Por lo que puedo entender, la lectura-escritura global de la semilla no es óptima para sistemas con muchos procesadores .

HashCode == 1: Cuenta los valores del código hash, no estoy seguro de en qué valor comienzan, pero parece bastante alto.

HashCode == 2: siempre devuelve exactamente el mismo código de hash de identidad de 1. Esto se puede utilizar para probar el código que se basa en la identidad del objeto. La razón por la que JavaChampionTest devolvió la URL de Kirk en el ejemplo anterior es que todos los objetos devolvieron el mismo código hash.

HashCode == 3: Cuenta los valores del código hash, comenzando desde cero. Es no parece ser seguro para subprocesos, por lo que varios subprocesos podrían generar objetos con el mismo código hash.

HashCode == 4: Esto parece tener alguna relación con la ubicación de memoria en la que se creó el objeto.

HashCode> = 5: Este es el algoritmo predeterminado para Java 8 y tiene una semilla por hilo . Utiliza el esquema xor-shift de Marsaglia para producir números pseudoaleatorios .

+0

Si estoy leyendo el código correctamente, en Java 8 & 9 la estrategia predeterminada es 5. –

1

Object.hashCode(), si la memoria no falla (ver el Javadoc de java.lang.Object), es dependiente de la implementación, y cambiará en función del objeto (la JVM de Sun deriva el valor del valor de la referencia al objeto).

Tenga en cuenta que si está implementando cualquier objeto no trivial y desea almacenarlos correctamente en un HashMap o HashSet, DEBE anular hashCode() y equals(). hashCode() puede hacer lo que quieras (es completamente legal, pero no es óptimo que devuelva 1.), pero es vital que si tu método equals() devuelve verdadero, el valor devuelto por hashCode() para ambos objetos sea igual.

La confusión y la falta de comprensión de hashCode() y equals() es una gran fuente de errores. Asegúrese de familiarizarse con los JavaDocs para Object.hashCode() y Object.equals(), y le garantizo que el tiempo invertido se amortizará solo.

5

leí que es una referencia a la memoria de un objeto ..

Object.hashCode() utiliza para devolver una dirección de memoria hace unos 14 años. No desde.

qué tipo de valor es

¿Qué es lo que depende enteramente de la clase que está hablando y si es o no se ha anulado `Object.hashCode().

+1

Esto no proporciona una respuesta a la pregunta. Para criticar o solicitar aclaraciones de un autor, deje un comentario debajo de su publicación. –

+0

@chsdk Basura. Refuta una afirmación hecha en la pregunta. 'No' es una respuesta. NB No estoy pidiendo aclaraciones de nadie tampoco. – EJP

+0

esto era un comentario automático, después de una bandera como "no una respuesta", que no estaba clara ni explicada antes de su última edición hace 2 horas. –

19

Si quiere saber cómo están implmentadas, le sugiero que lea la fuente. Si está utilizando un IDE, puede simplemente hacer + en un método que le interese y ver cómo se implementa un método. Si no puede hacer eso, puede buscar la fuente en google.

Por ejemplo, Entero.hashCode() se implementa como

public int hashCode() { 
     return value; 
    } 

y 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

He planeado responder exactamente de la misma manera; +1 – incarnate

+0

@ Peter Lawrey y cómo puedo ver la implementación Object hashCode –

+0

@naroji Está en OpenJDK. Lamentablemente, existen múltiples estrategias y no está claro cuál se utiliza. –

-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; 
/*  */ } 

Si realmente curiosa para aprender y luego ir a través de este código disponible en Hashing.class;

Aquí primera PARAM HASHING_SEED se calcula en base a continuación código

{ 
    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; 
} 

el segundo parámetro es matriz de caracteres de String, tercero siempre es '0' y cuarto uno es CHAR longitud de la matriz.

Y el cálculo anterior es solo para el código hash String.

Para todos los enteros, su código hash será su valor entero. Para caracteres (hasta dos letras) será código ASCII.

+0

No sé de dónde viene este código, pero no tiene nada que ver con la forma en que se calcula el '' String :: hashCode() 'de Java. O cualquier otro código de código estándar de Java ... AFAIK. El código * real * para 'String :: hashCode()' está aquí: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/String .java # String.hashCode% 28% 29 –

0

Tanto como sea razonablemente práctico, el método hashCode definido por la clase Object devuelve enteros distintos para objetos distintos. (Esto se implementa típicamente mediante la conversión de la dirección interna del objeto en un entero, pero esta técnica de implementación no es requerida por el lenguaje de programación Java ™.)

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

Cuestiones relacionadas