Как хэш-коды для перечислений вычисляются в Java и комбинируют хэш-коды enum для ключа HashMap
У меня есть класс, который содержит разные перечисления (разные типы). Этот класс используется как ключ для HashMap
. В настоящее время классы hashCode реализуются следующим образом:
public static class Key implements Comparable<Key> {
final int a;
final Enum1 enum1;
final Enum2 enum2;
@Override
public int hashCode() {
return a ^ enum1.hashCode() ^ enum2.hashCode();
}
// ... definition of equals and toString ...
}
Теперь, если enums hashCode просто вернет индекс значения enum в определении перечисления, это не будет оптимальным (слишком много столкновений). Определение метода для Enum.hashCode()
таково:
/**
* Returns a hash code for this enum constant.
*
* @return a hash code for this enum constant.
*/
public final int hashCode() {
return super.hashCode();
}
Предполагая, что это делегированные Object.hashCode()
, все должно быть хорошо, потому что для каждой константы перечисления существует только один экземпляр, а Object.hashCode()
теоретически будет чем-то вроде целого, полученного из внутреннего адреса объекта. Я прав?
PS: Конечно, вам придется использовать что-то более сложное, если один и тот же перечисление используется несколько раз в ключе.
Ответы
Ответ 1
Да, вы правы в том, что хэш-код элемента перечисления будет поступать из статического экземпляра, привязанного к ячейкам памяти и быть уникальным.
С другой стороны, существуют лучшие способы генерации хэш-кода с меньшей вероятностью столкновения. Проверьте, например, значения по умолчанию, которые eclipse может автогенерировать для вас (щелкните правой кнопкой мыши, Source > Generate hashCode и равно)
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((enum1 == null) ? 0 : enum1.hashCode());
result = prime * result + ((enum2 == null) ? 0 : enum2.hashCode());
return result;
}
Бросив простые числа в микс (точная математика ускользает от меня), вы должны быть немного более устойчивыми.
Заметьте, вы также можете позволить eclipse генерировать метод equals для вас! (Даже toString). Не говоря о том, что вы должны слепо доверять им, но обычно это очень хорошее начало.
Ответ 2
В Java 8 для этой цели вы можете использовать Objects.hash()
.
Например, вы можете переписать hashCode
на
//
import static java.util.Objects.hash;
//
@Override
public int hashCode() {
return hash(a, enum1, enum2);
}
Ответ 3
Просто протестировал это на JVM Oracle 1.6. Перечисление действительно делегирует Object.hashCode(). И он варьируется между разными тиражами. Имейте в виду, что ключи, таким образом, нестабильны между различными экземплярами виртуальных машин/виртуальных машин. Поэтому, когда вы сериализуете HashMap и читаете его на другой виртуальной машине, вы не сможете искать значения там, используя ключи, которые были созданы в этой виртуальной машине.
Ответ 4
Как сказано выше, Enum неизменяемы в Java,
Таким образом, hashcode, созданный для Enum, является идеальным ключом для коллекции Hash, так же как String - идеальные ключи.
Объявление enum - это особый вид объявления класса. Тип перечисления имеет общедоступные, самонастраиваемые члены для каждой из названных констант перечисления. Все классы перечисления имеют высококачественные методы toString, hashCode и equals. Все являются Serializable, Comparable и фактически окончательными. Ни один из них не является клонированным. Все "Объектные методы", кроме ofString, являются окончательными: мы заботимся о сравнении и сериализации и гарантируем t
шляпа сделана правильно.