2013-01-21 12 views
21

Jeśli uruchomisz następujące rozwiązania w 64-bitowej wersji HotSpot Java 7.Czy istnieje przyczyna Object.hashCode() jest 31-bitowy?

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

można uzyskać wynik jak

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

Zastanawiałem się, czy to oznacza, że ​​tylko naprawdę Object.hashCode() jest 31-bitowym i dlaczego to może być tak?


Nie jest tak, że górny bit nie jest używany. Od źródła do 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

To może być optymalizacja. Tabele skrótu często przyjmują kod skrótu, modyfikują liczbę segmentów, ale muszą najpierw maskować górny bit, aby uniknąć ujemnego indeksu. Być może jest to uniknięcie tego kroku później? – templatetypedef

+0

@templatetypedef Zastanawiam się, może to być przypadek, ale podejrzewam, że Object.hashCode() nie jest używany w kolekcjach Hash, które często są zwykle overriden z 32-bitowym hashCode(). Również HashMap mutuje surowy kod haszujący, więc używany jest bit górny. –

+2

@templatetypedef: ale co z nadpisaniami użytkowników, które tego nie robią? Tabela mieszania wciąż musi radzić sobie z tym –

Odpowiedz

13

HotSpot obsługuje wiele mieszania algorytmów Object. Jak odkryliśmy emprically górny bit jest zawsze zamaskowane przed zwracany jest wynik:

// 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 jest obliczana w następujący sposób:

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), 

Jak widać, markOopDesc::hash_mask zawsze ma nieco 31 ustawić na zero.

Co do tego, że tak się dzieje, twoje przypuszczenie jest równie dobre jak moje. Możliwe, że pierwotny programista uważał, że tylko radzenie sobie z dodatnimi liczbami całkowitymi uprości rzeczy w dół. Z tego, co wiemy, może to być nawet błąd "jeden po drugim" w obliczeniach hash_bits. ;-)

+0

+1 Jak zauważyła SWeko, C# robi to samo, co sugeruje, że mógł istnieć wspólny powód. Możliwe też, że skopiowali wspólną implementację, która zrobiła to z powodów, które już nie mają zastosowania. –

+0

Możliwe, że 'right_n_bits' nie bierze poprawnie 32 argumentów. Buduję Javę 8, więc mam kopię źródła, do której się odnosimy. ;) –

Powiązane problemy