>>>
- логическое смещение вправо (без расширения знака) ( JLS 15.19 Операторы сдвига ), а ^
- побитовое исключающее или ( JLS 15.22.1 Integer Bitwise Операторы ).
Относительно того, почему это делается, в документации есть подсказка: HashMap
использует таблицы длины степени двух и хэширует ключи, маскируя старшие биты и беря только младшие биты их хэш-кода.
// HashMap.java -- edited for conciseness
static int indexFor(int h, int length) {
return h & (length-1);
}
public V put(K key, V value) {
int hash = hash(key.hashCode());
int index = indexFor(hash, table.length);
// ...
}
Таким образом, hash()
пытается придать актуальность старшим битам, которые в противном случае были бы замаскированы (indexFor
в основном отбрасывает старшие биты h
и принимает только младшие k
биты, где length == (1 << k)
).
Сравните это с тем, как Hashtable
(у которого не должно быть таблицы длины степени двух) использует хеш-код ключа.
// Hashtable.java -- edited for conciseness
public synchronized V get(Object key) {
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % table.length;
// ...
}
При выполнении более дорогой операции %
(вместо простой битовой маскировки) производительность Hashtable
менее чувствительна к хеш-кодам с плохим распределением в младших битах (особенно, если table.length
- простое число) .