Скажем, у меня есть следующий класс:
class ABC {
private int myInt = 1;
private double myDouble = 2;
private String myString = "123";
private SomeRandomClass1 myRandomClass1 = new ...
private SomeRandomClass2 myRandomClass2 = new ...
//pseudo code
public int myHashCode() {
return 37 *
myInt.hashcode() *
myDouble.hashCode() *
... *
myRandomClass.hashcode()
}
}
Это было бы корректной реализацией хэш-кода? Это не то, как я обычно делаю это (я склонен следовать инструкциям Эффективного Java), но у меня всегда есть искушение просто сделать что-то как вышеупомянутый код.
Спасибо
Это зависит от того, что вы подразумеваете под «правильным». Предполагая, что вы используете hashCode ()
всех соответствующих полей, определяющих equals ()
, тогда да, это «правильно». Однако такие формулы, вероятно, не будут иметь хорошего распределения и, следовательно, скорее всего, вызовут больше конфликтов, чем в противном случае, что отрицательно скажется на производительности.
Вот цитата из Эффективное 2-е издание Java , пункт 9: Всегда переопределяйте hashCode
, когда вы переопределяете равным
, хотя рецепт в этом элементе дает достаточно хороший хэш функций, он не дает современных хэш-функций, а библиотеки платформы Java не предоставляют такие хеш-функции, как в версии 1.6. Написание таких хеш-функций - это тема исследования, которую лучше оставить математикам и компьютерным специалистам. [... Тем не менее] методы, описанные в этом пункте, должны подходить для большинства приложений.
Может не потребоваться много математических возможностей, чтобы оценить, насколько хороша ваша предложенная хеш-функция, но зачем вообще беспокоиться? Почему бы просто не последовать тому, что было анекдотично доказано на практике?
int
, называемой result
. int
c
для каждого поля:
, вычислить (f? 1: 0)
, char, short, int
, вычислить (int) f
long
, вычислить (int) (f ^ (f >>> 32))
float
, вычислить Float.floatToIntBits (f )
double
, вычислить Double.doubleToLongBits (f)
, затем хешировать полученное long
, как описано выше. равно
сравнивает поле, рекурсивно вызывая равно
, рекурсивно вызывает hashCode
в поле. Если значение поля равно null
, вернуть 0. Arrays.hashCode
, добавленных в выпуске 1.5. c
в результат
следующим образом: result = 31 * result + c;
Конечно, этот рецепт довольно сложен, но, к счастью, , вам не нужно заново реализовывать его каждый раз, благодаря java.util.Arrays.hashCode (Object [])
(и com.google.common.base.Objects
предоставляет удобный вариант варарг).
@Override public int hashCode() {
return Arrays.hashCode(new Object[] {
myInt, //auto-boxed
myDouble, //auto-boxed
myRandomClass,
});
}
Объект.hashCode ()
Это не требуется, чтобы если два объекта не равны согласно методу
equals (java.lang.Object)
, то вызовhashCode
] для каждого из двух объектов должен давать различные целочисленные результаты. Однако программист должен знать, что создание различных целочисленных результатов для неравных объектов может улучшить производительность хэш-таблиц.
Подобные действия разрешены контрактом. Но так всегда возвращается 1
. В HotSpot есть флаг времени компиляции, который всегда возвращает 1
для значения хеш-значения идентификатора. Однако такой выбор приведет к снижению производительности.
Есть особая проблема с умножением. Мало того, что хеш-значение 0 от компонента аннулирует значение, но и степени двойки будут постепенно обнулять младшие биты.
Проблема коммутативных операторов состоит в том, что перестановка значений приводит к конфликту.
Если существует определенная взаимосвязь между хеш-значениями компонентов, сложение будет особенно плохим. Например, конфликт (4, 6)
и (2, 8)
.
Мне кажется, что если вы не можете гарантировать, что продукт является простым числом, вы можете столкнуться (хотя, вероятно, редко) между res добавление хэш-кодов для объекта
Нет, но на практике это почти наверняка не очень хорошая идея. Самое главное - вам не разрешается изменять ни одно из полей, которые вы используете в хэш-коде. Все они должны быть постоянными.
Если вы измените одно из них, может произойти следующее: Вы вставляете объект в HashSet, изменяете поля, а затем проверяете, находится ли объект в HashSet. Хотя он там есть, из-за того, что хэш-код изменился, HashSet его не найдет!