Свойства хорошей функции ha sh включают 1) случайность, 2) однородность, 3) производительность, 4) масштабируемость. Небольшое количество коллизий не означает, что функция ha sh является достаточно случайной, например, в вашем тесте последовательные хеш-коды также не дают коллизий, но, очевидно, это не очень хорошая функция ha sh.
Также, Вы проверили только один случай потока. С одним потоком -XX:hashCode=0
(алгоритм Park-Miller RNG, который был по умолчанию до JDK 8) ведет себя довольно хорошо. Однако в приложениях с высокой степенью одновременности это становится ужасным: производительность снижается из-за высокой конкуренции за глобальную переменную, а вероятность генерирования одного и того же хэш-кода в разных потоках возрастает из-за состояния гонки, см. комментарий в исходном коде:
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
-XX:hashCode=1
также далеко не идеален с точки зрения случайности. Он просто XOR адрес объекта с глобальной переменной, обновляемой только в паузах JVM «остановка мира»:
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
Вы можете найти обсуждение и анализ различных алгоритмов hashCode в this поток электронной почты .
Короче говоря, только -XX:hashCode=0
и -XX:hashCode=5
обеспечивают хорошую случайность, в то время как последняя гораздо более масштабируема и производительна, так как использует только простые побитовые операции и не обновляет глобальные переменные.