Как нам выбрать лучшую реализацию метода hashCode() для коллекции (при условии, что метод equals был переопределен правильно)?
@Diablo Я не думаю, что это вообще ответ на вопрос - этот метод просто возвращает collection.hashCode() (hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/sha re /…)




Для простого класса часто проще всего реализовать hashCode () на основе полей класса, которые проверяются реализацией equals ().
public class Zam {
private String foo;
private String bar;
private String somethingElse;
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Zam otherObj = (Zam)obj;
if ((getFoo() == null && otherObj.getFoo() == null) || (getFoo() != null && getFoo().equals(otherObj.getFoo()))) {
if ((getBar() == null && otherObj. getBar() == null) || (getBar() != null && getBar().equals(otherObj. getBar()))) {
return true;
}
}
return false;
}
public int hashCode() {
return (getFoo() + getBar()).hashCode();
}
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
}
Самое важное - поддерживать согласованность hashCode () и equals (): если equals () возвращает true для двух объектов, то hashCode () должен возвращать то же значение. Если equals () возвращает false, то hashCode () должен возвращать другие значения.
Вроде SquareCog уже заметили. Если хэш-код генерируется один раз из конкатенации двух строк, чрезвычайно легко генерировать массу коллизий: ("abc"+""= = "ab"+"c"= = "a"+"bc"= = ""+"abc"). Это серьезный недостаток. Было бы лучше оценить хэш-код для обоих полей, а затем вычислить их линейную комбинацию (желательно с использованием простых чисел в качестве коэффициентов).
@ KrzysztofJabłoński Верно. Более того, замена foo и bar также приводит к ненужному столкновению.
@ about8: там довольно серьезная ошибка.
Zam obj1 = new Zam("foo", "bar", "baz");
Zam obj2 = new Zam("fo", "obar", "baz");
тот же хэш-код
ты, наверное, хочешь что-то вроде
public int hashCode() {
return (getFoo().hashCode() + getBar().hashCode()).toString().hashCode();
(вы можете получить hashCode прямо из int в Java в наши дни? Я думаю, что он выполняет автокастинг ... в этом случае пропустите toString, это некрасиво.)
ошибка содержится в длинном ответе about8.blogspot.com - получение хэш-кода из конкатенации строк оставляет вам хеш-функцию, которая одинакова для любой комбинации строк, составляющих одну и ту же строку.
Значит, это мета-обсуждение и никак не связано с вопросом? ;-)
Это исправление предложенного ответа, имеющее довольно существенный недостаток.
Это очень ограниченная реализация
Ваша реализация избегает проблемы и вводит другую; Замена foo и bar приводит к тому же hashCode. Ваш toString AFAIK не компилируется, а если и компилируется, то это ужасно неэффективно. Что-то вроде 109 * getFoo().hashCode() + 57 * getBar().hashCode() быстрее, проще и не вызывает ненужных коллизий.
Сначала убедитесь, что равенство реализовано правильно. От статья IBM DeveloperWorks:
- Symmetry: For two references, a and b, a.equals(b) if and only if b.equals(a)
- Reflexivity: For all non-null references, a.equals(a)
- Transitivity: If a.equals(b) and b.equals(c), then a.equals(c)
Затем убедитесь, что их связь с hashCode уважает контакт (из той же статьи):
- Consistency with hashCode(): Two equal objects must have the same hashCode() value
Наконец, хорошая хеш-функция должна стремиться приблизиться к идеальная хеш-функция.
Просто небольшое примечание для завершения другого более подробного ответа (с точки зрения кода):
Если я рассмотрю вопрос как-сделать-я-создать-хеш-таблицу-в-Java и особенно Запись часто задаваемых вопросов jGuru, я считаю, что некоторые другие критерии, по которым можно судить о хэш-коде, следующие:
Поскольку вы специально просили о коллекциях, я хотел бы добавить аспект, который еще не упоминался в других ответах: HashMap не ожидает, что их ключи изменят свой хэш-код после того, как они будут добавлены в коллекцию. Победит всю цель ...
Если я правильно понимаю ваш вопрос, у вас есть собственный класс коллекции (т.е. новый класс, который расширяется от интерфейса Collection), и вы хотите реализовать метод hashCode ().
Если ваш класс коллекции расширяет AbstractList, вам не о чем беспокоиться, уже существует реализация equals () и hashCode (), которая работает путем итерации по всем объектам и добавления их hashCodes () вместе.
public int hashCode() {
int hashCode = 1;
Iterator i = iterator();
while (i.hasNext()) {
Object obj = i.next();
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
return hashCode;
}
Теперь, если то, что вы хотите, является лучшим способом вычисления хэш-кода для определенного класса, я обычно использую оператор ^ (побитовое исключающее или) для обработки всех полей, которые я использую в методе equals:
public int hashCode(){
return intMember ^ (stringField != null ? stringField.hashCode() : 0);
}
любой метод хеширования, который равномерно распределяет хеш-значение по возможному диапазону, является хорошей реализацией. См. Эффективную java (http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=ru&sa=X&ult&result=news&result&result&result=ru&sa=X&result&result&result=news), там есть хороший совет для реализации хэш-кода (пункт 9, я думаю ...).
Лучшая реализация? Это сложный вопрос, потому что он зависит от модели использования.
A почти для всех случаев разумная хорошая реализация была предложена в Эффективная JavaДжош Блох в пункте 8 (второе издание). Лучше всего поискать это там, потому что автор объясняет там, почему такой подход хорош.
Создайте int result и назначьте значение ненулевой.
Для каждое полеf, протестированного в методе equals(), вычислите хэш-код c следующим образом:
boolean:
рассчитать (f ? 0 : 1);byte, char, short или int: вычислить (int)f;long: вычислить (int)(f ^ (f >>> 32));float: вычислить Float.floatToIntBits(f);double: вычислите Double.doubleToLongBits(f) и обработайте возвращаемое значение, как любое длинное значение;hashCode() или 0, если f == null;Объедините хэш-значение c с result:
result = 37 * result + c
Возврат result
Это должно привести к правильному распределению хеш-значений для большинства ситуаций использования.
Интересный ответ. Есть ли ссылка на математическое доказательство того, почему эта формула работает?
Да, мне особенно любопытно, откуда взялось число 37.
Я не знаю никаких доказательств. Число 37 произвольно, но должно быть простым. Почему? Я не совсем уверен, но это связано с артритом по модулю и свойствами простых чисел, которые приводят к распределению.
Спасибо за подробный ответ. Не могли бы вы рассказать мне, какой справочник / руководство вы использовали для создания этого набора советов? Также мне интересно, зачем умножать на 37? В чем магия в 37? Почему умножение лучше сдвига вправо (result = result << 16 + c)?
Я использовал пункт 8 книги Джоша Блоха «Эффективная Java».
@dma_k Причина использования простых чисел и метода, описанного в этом ответе, состоит в том, чтобы гарантировать, что вычисленный хэш-код будет уникальным. При использовании непростых чисел вы не можете этого гарантировать. Неважно, какое простое число вы выберете, в числе 37 нет ничего волшебного (жаль, что 42 не простое число, а?)
@ SimonAndréForsberg Ну, вычисленный хэш-код не всегда может быть уникальным :) Это хэш-код. Однако у меня возникла идея: у простого числа только один множитель, а у непростого - минимум два. Это создает дополнительную комбинацию для оператора умножения, чтобы получить тот же хэш, то есть вызвать коллизию.
@dma_k Да, это то, что я имел в виду :) Конечно, он не может быть уникальным все время (в противном случае для определения .equals было бы достаточно сравнения hashCode), использование простых чисел просто уменьшает коллизии.
Есть ли конкретная причина для сопоставления true с 0 и false с 1, а не наоборот?
@afsantos: Я ничего не могу придумать. Код из книги - это скорее общее руководство как закон и ни в коем случае не единственный правильный способ сделать это.
Я отредактировал сообщение, но это минимальное количество символов действительно раздражает при внесении незначительных изменений в разметку :( Если принято, может ли кто-нибудь удалить EDITED рядом с пунктом 1? Спасибо.
+1 Отличное объяснение, и мне очень нравится, что вы предоставили источник (то есть книгу Блоха, а не исходный код) на случай, если кому-то понадобится дополнительная информация.
Если поле f является массивом, у вас также есть возможность использовать методы java.util.Arrays.hashCode для вычисления хэш-значения этого поля.
Думаю Блох умножается на 31, а не на 37, для простота оптимизации.
Что, если поле является строкой? Это определенно объект, но какой должна быть реализация хэш-кода hte
Как рассчитать в случае объекта типа String Member
Одна из основных целей хеш-функции - сделать ее недорогой в оценке. Я просто написал хеш-функцию для структуры данных формальных параметров в компиляторе. Тест на равенство, конечно, должен проверять каждое имя и тип параметра после обеспечения структурной изометрии. Но что касается «Для каждого поля f, проверенного в методе equals ()», можно было бы найти хэш-поиск почти таким же дорогим, как линейный поиск. Вам нужно только создать тривиальную хеш-функцию, которая дает широкое распределение результатов по шаблонам использования. Помогают эмпирические измерения и эксперименты, а не предположения.
@Cope: Я не согласен с тем, что основная цель хеш-функции - скорость. Хотя есть определенные особые случаи для других реализаций hashCode, это очень хорошее начало. Безусловно, это правильная реализация. Большинство «умных» просто ошибаются.
Hej @dmeister - Я стою на своем. Все очень просто. 1. Основная цель хэш-функции (в структурах данных, а не, например, в шифровании) - скорость, чтобы избежать затрат на линейный поиск. 2. Существует компромисс между стоимостью вычисления хэша и стоимостью линейного поиска, и в многополевых структурах даже простого поиска по правильному ключу может быть достаточно, чтобы адекватно снизить стоимость. 3. Для больших структур данных (таких как мои формальные параметры) количество вычислений будет исчисляться десятками, если вы измеряете «каждое поле». 4. Универсального ответа не существует - значит, надо мерить.
Я видел, что 37 в другом месте для вычисления алгоритма хэш-кода строки и решил, что есть 26 букв и 10 цифр плюс символ пробела, что дает 37 lol
Не мог бы кто-нибудь объяснить, что делает эта строка в случае длинного? е ^ (е >>> 32)
В третьей версии Effective Java содержимое Пункт 8 не имеет ничего общего с хэш-кодом, поэтому, возможно, удалите упоминание об элементе.
Я думаю, что пункт 8 основан на втором издании. Третье издание имеет страницу в конце, где различные элементы соотносятся от выпуска к выпуску.
При хорошем хешировании вероятность хеш-коллизии должна быть 2 ^ -32, но при 37 вы можете легко столкнуться с коллизиями. Например, для объекта с двумя int (x, y) все хэш-коды для (1, -37), (0, 0), (-1, 37) будут иметь значение 0. Так что в реальной жизни (с маленькими значения гораздо более вероятны, чем большие), вероятность далека от 2 ^ -32. Лучше использовать простое число, например 912376189.
Эта функция обрабатывает double, как .Net, и мое недавнее тестирование показывает, что это очень плохо, когда ваши двойники не являются действительно случайными, а действительно целыми числами или имеют только один или два десятичных знака. Функция повторного хеширования из HashMap помогает, но все же не так хороша, как простое использование HashCode для int в этом случае.
В Apache Commons Lang есть хорошая реализация логики hashcode() и equals()Эффективная Java. Оформить заказ HashCodeBuilder и EqualsBuilder.
Обратной стороной этого API является то, что вы оплачиваете стоимость создания объекта каждый раз, когда вызываете equals и hashcode (если только ваш объект не является неизменяемым и вы предварительно вычисляете хэш), что в некоторых случаях может быть очень много.
до недавнего времени это был мой любимый подход. Я столкнулся с StackOverFlowError при использовании критерия для ассоциации SharedKey OneToOne. Более того, класс Objects предоставляет методы hash(Object ..args) и equals() начиная с Java7. Они рекомендуются для любых приложений, использующих jdk 1.7+.
@Diablo Я думаю, ваша проблема заключалась в цикле в графе объектов, а затем вам не повезло с большей частью реализации, поскольку вам нужно игнорировать некоторую ссылку или прервать цикл (требуя IdentityHashMap). FWIW Я использую хэш-код на основе идентификатора и равенство для всех сущностей.
about8.blogspot.com, вы сказали
if equals() returns true for two objects, then hashCode() should return the same value. If equals() returns false, then hashCode() should return different values
Я не могу с тобой согласиться. Если два объекта имеют одинаковый хэш-код, это не обязательно означает, что они равны.
Если A равно B, то A.hashcode должен быть равен B.hascode
но
если A.hashcode равно B.hascode, это не означает, что A должно быть равно B
Если (A != B) and (A.hashcode() == B.hashcode()), это то, что мы называем конфликтом хэш-функции. Это потому, что codomain хэш-функции всегда конечен, а его домен обычно нет. Чем больше кодомен, тем реже должно происходить столкновение. Хорошая хеш-функция должна возвращать разные хеши для разных объектов с наибольшей вероятностью, достижимой при конкретном размере кодомена. Однако это редко может быть полностью гарантировано.
Это должен быть просто комментарий к вышеуказанному посту для Грея. Хорошая информация, но не совсем отвечает на вопрос
Хорошие комментарии, но будьте осторожны при использовании термина «разные объекты» ... потому что equals () и, следовательно, реализация hashCode () не обязательно относятся к разным объектам в объектно-ориентированном контексте, но обычно больше относятся к их представлениям модели предметной области (например, два люди могут считаться одинаковыми, если у них общий код страны и идентификатор страны - хотя это могут быть два разных «объекта» в JVM - они считаются «равными» и имеют заданный хэш-код) ...
Я предпочитаю использовать служебные методы из Библиотека Google Collections из класса Objects, которые помогают мне поддерживать мой код в чистоте. Очень часто методы equals и hashcode создаются из шаблона IDE, поэтому их трудно читать.
Используйте методы отражения в Apache Commons EqualsBuilder и HashCodeBuilder.
Если вы собираетесь использовать это, имейте в виду, что отражение стоит дорого. Честно говоря, я бы не стал использовать это ни для чего, кроме как выбросить код.
Если вы используете eclipse, вы можете сгенерировать equals() и hashCode(), используя:
Source -> Generate hashCode() and equals().
Используя эту функцию, вы можете выбрать какие поля, который вы хотите использовать для вычисления равенства и хэш-кода, и Eclipse генерирует соответствующие методы.
Лучше использовать функциональность, предоставляемую Eclipse, которая неплохо справляется со своей задачей, и вы можете приложить свои усилия и энергию для разработки бизнес-логики.
+1 Хорошее практическое решение. Решение dmeister более комплексное, но я часто забываю обрабатывать нули, когда пытаюсь написать хэш-коды самостоятельно.
+1 Согласен с Quantum7, но я бы сказал, что также очень хорошо понимать, что делает реализация, созданная Eclipse, и откуда она берет детали своей реализации.
Извините, но ответы, касающиеся «функциональности, предоставляемой [некоторой IDE]», на самом деле не актуальны в контексте языка программирования в целом. Существуют десятки IDE, и это не отвечает на вопрос ... а именно потому, что это больше связано с алгоритмическим определением и напрямую связано с реализацией equals () - то, о чем IDE ничего не знает.
При объединении хеш-значений я обычно использую метод объединения, который используется в библиотеке boost C++, а именно:
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
Это достаточно хорошо для обеспечения равномерного распределения. Некоторое обсуждение того, как работает эта формула, можно найти в сообщении StackOverflow: Магическое число в бусте :: hash_combine
Хорошее обсуждение различных хеш-функций: http://burtleburtle.net/bob/hash/doobs.html
Это вопрос о Java, а не о C++.
Если вас устраивает эффективная реализация Java, рекомендованная dmeister, вы можете использовать вызов библиотеки вместо собственного:
@Override
public int hashCode() {
return Objects.hashCode(this.firstName, this.lastName);
}
Для этого требуется либо Guava (com.google.common.base.Objects.hashCode), либо стандартная библиотека в Java 7 (java.util.Objects.hash), но работает одинаково.
Если у кого-то нет веской причины не использовать их, нужно обязательно использовать их в любом случае. (Формулируя это сильнее, поскольку это должно быть сформулировано ИМХО.) Применяются типичные аргументы в пользу использования стандартных реализаций / библиотек (лучшие практики, хорошо протестированные, менее подверженные ошибкам и т.д.).
Случай, чтобы не использовать реализацию библиотеки, - это когда вы реализовали собственный метод Object # equals (Object obj). См. JavaDoc Object # hashcode (): «Если два объекта равны в соответствии с методом equals (Object), то вызов метода hashCode для каждого из двух объектов должен привести к одинаковому целочисленному результату». Использование любой из этих реализаций библиотеки сгенерирует хэш-код «псевдо» адреса памяти, который может нарушить описанное требование.
@ justin.hughey вы, кажется, запутались. Единственный случай, когда вы должны переопределить hashCode, - это если у вас есть собственный equals, и именно для этого предназначены эти библиотечные методы. В документации довольно четко описано их поведение по отношению к equals. Реализация библиотеки не претендует на то, чтобы вы не знали, каковы характеристики правильной реализации hashCode - эти библиотеки делают Полегче для вас, чтобы реализовать такую соответствующую реализацию в большинстве случаев, когда equals переопределяется.
Ты совершенно прав. Спасибо, что указали на это, я не знаю, откуда я пришел по этому поводу. :)
Для всех разработчиков Android, которые смотрят на класс java.util.Objects, он был представлен только в API 19, поэтому убедитесь, что вы работаете на KitKat или более поздней версии, иначе вы получите NoClassDefFoundError.
Лучший ответ IMO, хотя в качестве примера я бы предпочел метод JDK7 java.util.Objects.hash(...), чем метод guava com.google.common.base.Objects.hashCode(...). Я думаю, что большинство людей предпочли бы стандартную библиотеку дополнительной зависимости.
Если есть два или более аргумента и если какой-либо из них является массивом, результат может быть не таким, как вы ожидаете, потому что hashCode() для массива - это просто его java.lang.System.identityHashCode(...).
@starikoff Да, вспомогательные методы не освобождают вас от необходимости понимать, что вы делаете. Они просто помогают избежать определенных ошибок и дублирования кода.
@bacar Конечно. Я просто оставил свой комментарий, чтобы тот, кто узнает об этих методах из этого поста, также узнал об их ограничениях.
Комментарии об ассоциации переопределения equals () верны. hashCode () не реализуется в вакууме и должен быть напрямую связан с тем, как equals () реализован в документации (два объекта могут иметь один и тот же hashCode, но НЕ быть равными - но два равных объекта ДОЛЖНЫ иметь одинаковый hashCode).
@Kissaki Напротив: используя стандартную реализацию приводит к стандартным коллизиям.
@AndrewKelly Прошло несколько лет, но для всех, кто сталкивался с этим и все еще поддерживает <19, теперь есть ObjectsCompat.hash(...), и его реализация - это просто Arrays.hashCode(...) для API <19, что похоже на ту же реализацию в Objects.hash(...). В Android Studio есть генератор кода для этого, который вставит Objects.hash(...), хотя независимо от того, какой у вас minSdkVersion, поэтому разработчики должны знать, что они могут просто заменить его на ObjectsCompat, и все будет в порядке.
Objects.hashCode () - это один из API-интерфейсов, который теперь автоматически удаляется D8 в Android Studio 4+, поэтому вы можете использовать исходный API для целей, предшествующих API 19 (нет необходимости в ObjectsCompat). Полный список API-интерфейсов с обессахиванием см. В этом файле. jakewharton.com/static/files/d8_api_desugar_list.txt
Хотя это связано с Документация Android (Wayback Machine) и Мой собственный код на Github, оно будет работать для Java в целом. Мой ответ - расширение Ответ dmeister с помощью простого кода, который намного легче читать и понимать.
@Override
public int hashCode() {
// Start with a non-zero constant. Prime is preferred
int result = 17;
// Include a hash for each field.
// Primatives
result = 31 * result + (booleanField ? 1 : 0); // 1 bit » 32-bit
result = 31 * result + byteField; // 8 bits » 32-bit
result = 31 * result + charField; // 16 bits » 32-bit
result = 31 * result + shortField; // 16 bits » 32-bit
result = 31 * result + intField; // 32 bits » 32-bit
result = 31 * result + (int)(longField ^ (longField >>> 32)); // 64 bits » 32-bit
result = 31 * result + Float.floatToIntBits(floatField); // 32 bits » 32-bit
long doubleFieldBits = Double.doubleToLongBits(doubleField); // 64 bits (double) » 64-bit (long) » 32-bit (int)
result = 31 * result + (int)(doubleFieldBits ^ (doubleFieldBits >>> 32));
// Objects
result = 31 * result + Arrays.hashCode(arrayField); // var bits » 32-bit
result = 31 * result + referenceField.hashCode(); // var bits » 32-bit (non-nullable)
result = 31 * result + // var bits » 32-bit (nullable)
(nullableReferenceField == null
? 0
: nullableReferenceField.hashCode());
return result;
}
РЕДАКТИРОВАТЬ
Обычно, когда вы переопределяете hashcode(...), вы также хотите переопределить equals(...). Итак, для тех, кто будет или уже внедрил equals, вот хороший справочник из моего Github ...
@Override
public boolean equals(Object o) {
// Optimization (not required).
if (this == o) {
return true;
}
// Return false if the other object has the wrong type, interface, or is null.
if (!(o instanceof MyType)) {
return false;
}
MyType lhs = (MyType) o; // lhs means "left hand side"
// Primitive fields
return booleanField == lhs.booleanField
&& byteField == lhs.byteField
&& charField == lhs.charField
&& shortField == lhs.shortField
&& intField == lhs.intField
&& longField == lhs.longField
&& floatField == lhs.floatField
&& doubleField == lhs.doubleField
// Arrays
&& Arrays.equals(arrayField, lhs.arrayField)
// Objects
&& referenceField.equals(lhs.referenceField)
&& (nullableReferenceField == null
? lhs.nullableReferenceField == null
: nullableReferenceField.equals(lhs.nullableReferenceField));
}
Документация Android теперь больше не включает приведенный выше код, поэтому вот кешированная версия из Wayback Machine - документация для Android (7 февраля 2015 г.)
Вот еще одна демонстрация подхода JDK 1.7+ с учётом логики суперкласса. Я считаю это довольно удобным с учётом класса объекта hashCode (), чистой зависимостью от JDK и без дополнительной ручной работы. Обратите внимание, что Objects.hash() является нулевым допуском.
Я не включил какую-либо реализацию equals(), но на самом деле она вам, конечно, понадобится.
import java.util.Objects;
public class Demo {
public static class A {
private final String param1;
public A(final String param1) {
this.param1 = param1;
}
@Override
public int hashCode() {
return Objects.hash(
super.hashCode(),
this.param1);
}
}
public static class B extends A {
private final String param2;
private final String param3;
public B(
final String param1,
final String param2,
final String param3) {
super(param1);
this.param2 = param2;
this.param3 = param3;
}
@Override
public final int hashCode() {
return Objects.hash(
super.hashCode(),
this.param2,
this.param3);
}
}
public static void main(String [] args) {
A a = new A("A");
B b = new B("A", "B", "C");
System.out.println("A: " + a.hashCode());
System.out.println("B: " + b.hashCode());
}
}
Стандартная реализация слабая, и ее использование приводит к ненужным конфликтам. Представьте себе
class ListPair {
List<Integer> first;
List<Integer> second;
ListPair(List<Integer> first, List<Integer> second) {
this.first = first;
this.second = second;
}
public int hashCode() {
return Objects.hashCode(first, second);
}
...
}
Сейчас же,
new ListPair(List.of(a), List.of(b, c))
и
new ListPair(List.of(b), List.of(a, c))
имеют тот же hashCode, а именно 31*(a+b) + c, поскольку множитель, используемый для List.hashCode, здесь используется повторно. Очевидно, столкновения неизбежны, но создавать ненужные столкновения просто ... излишне.
Нет ничего особенного в использовании 31. Множитель должен быть нечетным, чтобы избежать потери информации (любой четный множитель теряет по крайней мере самый старший бит, кратные четырем теряют два и т. д.). Можно использовать любой нечетный множитель. Небольшие множители могут привести к более быстрым вычислениям (JIT может использовать сдвиги и добавления), но, учитывая, что умножение имеет задержку всего в три цикла на современных Intel / AMD, это вряд ли имеет значение. Маленькие множители также приводят к большему количеству конфликтов для небольших входов, что иногда может быть проблемой.
Использование простого числа бессмысленно, поскольку простые числа не имеют смысла в кольце Z / (2 ** 32).
Итак, я бы рекомендовал использовать случайно выбранное большое нечетное число (не стесняйтесь брать простое). Поскольку процессоры i86 / amd64 могут использовать более короткие инструкции для подгонки операндов в один знаковый байт, есть небольшое преимущество в скорости для таких множителей, как 109. Для минимизации коллизий возьмите что-то вроде 0x58a54cf5.
Использование разных множителей в разных местах полезно, но, вероятно, недостаточно, чтобы оправдать дополнительную работу.
с Java 7+, я думаю,
Objects.hashCode(collection)должен быть идеальным решением!