2013-01-21 9 views
21

Se si esegue quanto segue su HotSpot Java 7 versione a 64 bit.C'è una ragione per cui Object.hashCode() è a 31 bit?

int countTopBit = 0, countLowestBit = 0; 
for (int i = 0; i < 100000000; i++) { 
    int h = new Object().hashCode(); 
    if (h < 0) 
     countTopBit++; 
    if ((h & 1) == 1) 
     countLowestBit++; 
} 
System.out.println("The count of negative hashCodes was " + countTopBit + ", the count of odd hashCodes was " + countLowestBit); 

si può ottenere un risultato come

The count of negative hashCodes was 0, the count of odd hashCodes was 49994232 

mi chiedevo se questo significa che il Object.hashCode() è veramente solo 31-bit e il motivo per cui questo potrebbe essere così?


Non è il caso che il bit superiore non sia utilizzato. Dalla sorgente per HashMap

257 /** 
258 * Applies a supplemental hash function to a given hashCode, which 
259 * defends against poor quality hash functions. This is critical 
260 * because HashMap uses power-of-two length hash tables, that 
261 * otherwise encounter collisions for hashCodes that do not differ 
262 * in lower bits. Note: Null keys always map to hash 0, thus index 0. 
263 */ 
264 static int hash(int h) { 
265  // This function ensures that hashCodes that differ only by 
266  // constant multiples at each bit position have a bounded 
267  // number of collisions (approximately 8 at default load factor). 
268  h ^= (h >>> 20)^(h >>> 12); 
269  return h^(h >>> 7)^(h >>> 4); 
270 } 
+0

Questo potrebbe essere un'ottimizzazione. Le tabelle hash prendono spesso il codice hash mod il numero di bucket, ma devono prima mascherare il primo bit per evitare un indice negativo. Forse questo è per evitare quel passo dopo? – templatetypedef

+0

@templatetypedef Mi sono chiesto se questo potrebbe essere il caso, ma ho il sospetto che Object.hashCode() non sia usato negli insiemi di hash che spesso è sovrascritto da hashCode() a 32 bit. Anche HashMap muta il hashCode raw in modo che venga usato il bit più in alto. –

+2

@templatetypedef: ma per quanto riguarda le sostituzioni dell'utente che non lo fanno? La tabella hash deve ancora occuparsi di questo codice –

risposta

13

HotSpot supporta una varietà di algoritmi di hashing per Object. Come hai scoperto emprically, il bit superiore è sempre mascherato fuori prima che il risultato viene restituito:

// src/share/vm/runtime/synchronizer.cpp 
static inline intptr_t get_next_hash(Thread * Self, oop obj) { 
    ... 
    value &= markOopDesc::hash_mask; 
    ... 
    return value; 
} 

markOopDesc::hash_mask è calcolata come segue:

enum { age_bits     = 4, 
     lock_bits    = 2, 
     biased_lock_bits   = 1, 
     max_hash_bits   = BitsPerWord - age_bits - lock_bits - biased_lock_bits, 
     hash_bits    = max_hash_bits > 31 ? 31 : max_hash_bits, 
     ... 
     hash_mask    = right_n_bits(hash_bits), 

Come si può vedere, markOopDesc::hash_mask ha sempre bit 31 impostato su zero.

Per quanto riguarda il motivo per cui questo è fatto, la tua ipotesi è buono come il mio. Potrebbe essere stato che lo sviluppatore originale riteneva che trattare solo con numeri interi positivi avrebbe semplificato le cose lungo la linea. Per quel che ne sappiamo, potrebbe anche essere un errore "off-one" nel calcolo hash_bits. ;-)

+0

+1 Come trovato da SWeko, C# fa la stessa cosa che suggerisce che ci potrebbe essere stata una ragione comune. O potrebbe essere copiato un'implementazione comune che ha fatto questo per ragioni che non si applicano più. –

+0

Potrebbe essere possibile che 'right_n_bits' non consideri correttamente 32 come argomento. Sto costruendo Java 8 così ho una copia della fonte a cui fare riferimento. ;) –

Problemi correlati