Во-первых, важно определиться с тем, что генерируемый по умолчанию хэш не соответствует требованиям к хорошему хэшу. Поэтому метод hashCode всегда надо переопределять в своих классах. Во-вторых, важно понимать, что способ генерации хэшкода по умолчанию не оговорен в спецификаций Java, а потому может отличать в разных реализациях виртуальной машины или даже в разных версиях одной и той же реализации. Я дальше буду писать про HotSpot.
Несмотря на то, что в
документации написано, что генерируемый по умолчанию хэш - "this is typically implemented by converting the internal address of the object into an integer", этот
internal address не имеет никакого отношения к положению объекта в памяти. Попробуем разобраться откуда он берётся. Для этого заглянем в исходный код
Object. Как видно, hashCode() - это нативный метод, а значит придётся покопаться в сишном коде. Находим его
определение:
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
JVMWrapper("JVM_IHashCode");
// as implemented in the classic virtual machine; return 0 if object is NULL
return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END
Ага, хэшкод генерирует функция ObjectSynchronizer::FastHashCode(). Посмотрим, что
в ней. Интересная часть:
mark = monitor->header();
...
hash = mark->hash();
if (hash == 0) {
hash = get_next_hash(Self, obj);
...
}
...
return hash;
Функция пытается получить хэш из заголовка объекта, а если его там нет, то генерирует вызовом
get_next_hash. В этой функции определяются несколько методов генерации хэша. В HotSpot шестой и седьмой версии использовался первый - генерация случайного числа. Вообще ни разу не
internal address! С восьмой версии используется пятый - "Marsaglia's xor-shift scheme with thread-specific state". Почитать про этот алгоритм можно
здесь. Если отбросить нюансы, опять случайное число, не
internal address.
Если я переопределю хешкод и он иногда ( или всегда, например -1) будет выдавать отрицательные целые, что будет? Очевидно это число как-то преобразуется под капотом? потому что должен выдаваться номер бакета?
Заглядываем в
исходный код HashMap
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
Переменная
n
в этой точке равна количеству бакетов - положительному числу, а значит
i
тоже будет положительным числом.
3. Учитывается заполнение всех бакетов. В вашем случае, когда элементы возвращают один и тот же хэш, а значит скапливаются в одном бакете, будет учитываться другой параметр - TREEIFY_THRESHOLD. По умолчанию он равен 8 и после накопления в бакете стольки элементов, он будет преобразован из списка в дерево.
P.S. Виртуальная машина
Zing генерирует хэшкоды по умолчанию на основе адреса объекта в памяти.