20 ответов:
лучшая реализация? Это сложный вопрос, потому что это зависит от шаблона использования.
A почти во всех случаях разумная хорошая реализация была предложена в Джош Блох ' s Эффективная Java в пункте 8 (второе издание). Лучше всего искать его там, потому что автор объясняет там, почему это хорошо.
короткая версия
создать
int resultи назначьте ненулевой значение.на поле
fпроверен вequals()метод, вычислить хэш-кодcby:
- если поле f является
boolean: вычислить(f ? 0 : 1);- если поле f является
byte,char,shortилиint: расчета(int)f;- если поле f является
long: расчета(int)(f ^ (f >>> 32));- если поле f является
float: вычислитьFloat.floatToIntBits(f);- если поле f является
double: расчетаDouble.doubleToLongBits(f)и обрабатывать возвращаемое значение как каждое длинное значение;- если поле f является объект: используйте результат
hashCode()способ или 0, еслиf == null;- если поле f является массив: см. каждое поле как отдельный элемент и вычислить хэш-значение рекурсивные моды и объединить значения как описано следующий.
объединить значение хэша
cСresult:result = 37 * result + cвозвращение
resultэто должно привести к правильному распределению хэш-значений для большинства ситуаций использования.
Если вы довольны эффективной реализацией Java, рекомендованной dmeister, вы можете использовать вызов библиотеки вместо сворачивания собственного:
@Override public int hashCode(){ return Objects.hashCode(this.firstName, this.lastName); }для этого требуется либо гуава (
com.google.common.base.Objects.hashCode(...)) или JDK7 (java.util.Objects.hash(...)) но работает так же.
лучше использовать функциональность, предоставляемую Eclipse, которая делает довольно хорошую работу, и вы можете приложить свои усилия и энергию в разработке бизнес-логики.
хотя это связано с
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; }EDIT
как правило, при переопределении
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)); }
сначала убедитесь, что equals реализован правильно. От статья IBM DeveloperWorks:
- симметрия: для двух ссылок, a и b, A. равно(b) тогда и только тогда, когда b.равно(a)
- рефлексивность: для всех ненулевых ссылок a. equals (a)
- транзитивность: если a. равно(b) и b.равно(c), то a.равно(c)
затем убедитесь, что их отношение с хэш-кодом уважает контакт (из той же статьи):
- согласованность с hashCode (): два одинаковых объекта должны иметь одинаковое значение hashCode ()
наконец-то хорошая хэш-функция должна стремиться приблизиться к идеальная хэш-функция.
about8.blogspot.com - ты сказал
Если equals() возвращает true для двух объектов, то hashCode () должен возвращать то же значение. Если equals() возвращает false, то hashCode () должен возвращать разные значения
Я не могу согласиться с вами. Если два объекта имеют одинаковый хэш-код, это не значит, что они равны.
Если A равно B, то A. hashcode должен быть равен B. hascode
но
Если A. hashcode равен B. hascode это не значит, что A должен равняться B
есть хорошая реализация Эффективная Java ' s
hashcode()иequals()логика Apache Commons Lang. Оформить заказ HashCodeBuilder и EqualsBuilder.
Если вы используете Eclipse, вы можете создать
equals()иhashCode()использование:Source - > Generate hashCode () and equals ().
С помощью этой функции вы можете решить поля вы хотите использовать для вычисления равенства и хэш-кода, и Eclipse генерирует соответствующие методы.
просто быстрая заметка для завершения другого более подробного ответа (в терминах кода):
Если я рассматриваю вопрос как-делать-Я-создание хэш-таблица-в-Ява и особенно jGuru статье, Я считаю, что некоторые другие критерии, по которым можно судить о хэш-коде:
- синхронизация (поддерживает ли algo параллельный доступ или нет)?
- fail safe итерация (algo обнаруживает коллекцию, которая изменяется во время итерация)
- значение null (поддерживает ли хэш-код значение null в коллекции)
Если я правильно понял ваш вопрос, у вас есть пользовательский класс коллекции (т. е. новый класс, который простирается от интерфейса Collection) и вы хотите реализовать метод hashCode ().
Если ваш класс коллекции расширяет AbstractList, то вам не нужно беспокоиться об этом, уже есть реализация equals() и hashCode (), которая работает путем перебора всех объектов и добавления их хэш-кодов() вместе.
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; }
@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();(можете ли вы получить хэш-код непосредственно из int в Java в эти дни? Я думаю, что это делает некоторые автокастинг.. если это так, пропустите toString, это уродливо.)
поскольку вы специально попросили коллекции, я хотел бы добавить аспект, который другие ответы еще не упоминали: HashMap не ожидает, что их ключи изменят свой хэш-код, как только они будут добавлены в коллекцию. Это разрушило бы всю цель...
используйте методы отражения на Apache Commons EqualsBuilder и HashCodeBuilder.
любой метод хэширования, который равномерно распределяет значение хэша по возможному диапазону, является хорошей реализацией. См. раздел эффективная java ( http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=en&sa=X&oi=book_result&resnum=1&ct=result), есть хороший совет там для реализации хэш-кода (пункт 9 я думаю...).
Я предпочитаю использовать служебные методы fromm Google Collections lib from class Objects это помогает мне держать мой код в чистоте. Очень часто
equalsиhashcodeметоды сделаны из шаблона IDE, поэтому их не чисты для чтения.
Я использую крошечную обертку вокруг
Arrays.deepHashCode(...)потому что он обрабатывает массивы поставляются в качестве параметров правильноpublic static int hash(final Object... objects) { return Arrays.deepHashCode(objects); }
вот еще одна демонстрация подхода JDK 1.7+ с учетом логики суперкласса. Я вижу, что это довольно удобно с учетом класса объектов hashCode (), чистой зависимостью JDK и без дополнительной ручной работы. Пожалуйста, обратите внимание
Objects.hash()имеет значение null терпимо.Я не включил ни одного
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.
использование разных множителей в разных местах полезно, но, вероятно, недостаточно, чтобы оправдать дополнительную работу.
при объединении хэш-значений я обычно использую метод объединения, который используется в библиотеке boost c++, а именно:
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);это делает довольно хорошую работу по обеспечению равномерного распределения. Для некоторого обсуждения того, как эта формула работает, см. сообщение StackOverflow: магическое число в boost:: hash_combine
есть хорошее обсуждение различных хэш-функций по адресу:http://burtleburtle.net/bob/hash/doobs.html
для простого класса часто проще всего реализовать 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 () должен возвращать разные значения.
Comments