2. Метод hashCode()
Переопределение equals и hashCode
Переопределение equals и hashCode
Есть у класса Object такой метод, как equals, который используется для сравнения двух объектов. По умолчанию он выглядит так:
Т.е. просто сравниваются две ссылки(прямо как оператор "=="). Понятно, что обычно, это не то, что нам надо. Обычно два объекта равны, если основные данные их равны. Чтобы это понимали не только мы, но и компилятор, нам и нужно переопределять метод equals. Существует несколько свойств, которым должен удовлетворять наш переопределенный метод, а именно:
Очевидно, нам надо, чтобы equals возвращал true тогда и только тогда, когда поля x у объектов равны. Что ж, переопределенный метод будет выглядеть примерно так:
Кстати, не забывайте, что тип аргумента должен быть именно Object, а не MyClass. В этом случае это будет уже не переопределение(overriding), а перегрузка(overloading). Именно поэтому, при переопределении следует использовать конструкцию @Override, она проверит, действительно ли ваш метод переопределяет метод суперкласса, и, если это не так, выдаст ошибку.
Пройдемся по коду. Сначала идет проверка на свойство №2(рефлексивность). Затем мы должны сравнить наши поля x, но т.к. в метод передается Object, то нам надо делать приведение типа. А вдруг нам передали не MyClass? Тут нам пригодится instanceof. x instanceof y проверяет: является ли x экземпляром y. Однако конструкция instanceof не только позволяет нам отбросить неверные объекты, но и проверяет наше свойство о null-объектах, т.к. x instanceof y возвращает false, если x - null. Ну а дальше мы непосредственно проверяем наши поля на равенства.
И вроде все бы ничего, переопределили equals, всё работает. Однако, переопределяя equals вы должны позаботиться еще об одном методе класса Object:
Существуют следующие соглашения, относительно этих двух методов:
Метод hashCode используется в hash-коллекциях(например HashSet), и чем меньше будет коллизий(одинаковый код при разных объектах) тем эффективнее эти коллекции будут работать с объектами вашего класса.
В приведенном выше классе MyClass хэш-кодом может выступать само значение x. Это совсем тривиальный случай. А что, если у нас, наряду с числом x, будет например строка s.
Часто используют примерно такой подход:
1
2
3
| public boolean equals(Object obj){ return ( this == obj); } |
- Симметричность. Т.е. если для каких-либо объектов x и y x.equals(y) возвращает true, то и y.equals(x) должен возвращать true.
- Рефлексивность. Для любого объекта x x.equals(x) должен возвращать true.
- Постоянство. Для любых объектов x и y x.equals(y) возвращает одно и тоже, если информация, используемая в сравнениях, не меняется.
- Транзитивность. Для любых объектов x, y и z, если x.equals(y) вернет true и y.equals(z) вернет true, то и x.equals(z) должен вернуть true.
- Для любого не null объекта x x.equals(null) должен возвращать false.
1
2
3
4
5
| public class MyClass{ private int x; public MyClass( int x){ this .x = x; } public int getX(){ return x; } } |
1
2
3
4
5
6
7
8
9
| @Override public boolean equals(Object obj){ if ( this == obj) return true ; if (!(obj instanceof MyClass)) return false ; MyClass myClass = (MyClass)obj; return (x == myClass.getX()); } |
Пройдемся по коду. Сначала идет проверка на свойство №2(рефлексивность). Затем мы должны сравнить наши поля x, но т.к. в метод передается Object, то нам надо делать приведение типа. А вдруг нам передали не MyClass? Тут нам пригодится instanceof. x instanceof y проверяет: является ли x экземпляром y. Однако конструкция instanceof не только позволяет нам отбросить неверные объекты, но и проверяет наше свойство о null-объектах, т.к. x instanceof y возвращает false, если x - null. Ну а дальше мы непосредственно проверяем наши поля на равенства.
И вроде все бы ничего, переопределили equals, всё работает. Однако, переопределяя equals вы должны позаботиться еще об одном методе класса Object:
1
| public native int hashCode() |
- Всякий раз, когда метод вызывается у одного и того же объекта во время выполнения приложения, он должен возвращать одно и то же число, если используемая информация не изменяется. hashCode может возвращать разные значения для идентичных объектов в различных экземплярах приложения.
- Если два объекта равны, согласно equals, то их hashCode должны возвращать одинаковые значения.
- Обратное требование необязательно. Два неравных объекта могут возвращать одинаковый hashCode. Однако для повышения производительности, лучше, чтобы разные объекты возвращали разные коды.
Метод hashCode используется в hash-коллекциях(например HashSet), и чем меньше будет коллизий(одинаковый код при разных объектах) тем эффективнее эти коллекции будут работать с объектами вашего класса.
В приведенном выше классе MyClass хэш-кодом может выступать само значение x. Это совсем тривиальный случай. А что, если у нас, наряду с числом x, будет например строка s.
Часто используют примерно такой подход:
1
2
3
4
5
6
7
8
| @Override public int hashCode(){ int code = 11 ; int k = 7 ; code = k*code + x; code = k*code + s.hashCode(); return code; } |