Objects.hash() vs Objects.hashCode(), необходимо уточнение
в Java 7 у нас есть
o.hashCode();
Objects.hashCode(o);
Objects.hash(o);
Первые 2 примерно одинаковы с проверкой нулевой точки, но что является последним?
Когда указана одна ссылка на объект, возвращаемое значение не равно хеш-коду этой ссылки на объект.
Это почему? Я имею в виду, что нам не нужны 3 метода, которые делают одно и то же, я это понимаю, но зачем вообще нужен Objects.hash()
? Когда бы вы решили использовать один против другого?
Ответы
Ответ 1
Смотрите документацию для hashCode
и hash
. hash
принимает Object...
в то время как hashCode
- Object
hashCode
принимает Object
. Приведенный пример:
@Override public int hashCode() {
return Objects.hash(x, y, z);
}
-
Objects.hash(Object... values)
следует использовать в тех случаях, когда вы хотите хеш последовательности объектов, например, при определении собственного метода hashCode
и хотите просто закодировать хеш для нескольких значений, которые составляют идентичность вашего объект. -
Objects.hashCode(Object o)
следует использовать, когда вы хотите хеш одного объекта, без броска, если объект нулевой. -
Object::hashCode()
следует использовать, когда вы хотите хеш одного объекта, и будет выдавать исключение, если объект имеет значение null.
Обратите внимание, что hash(o)
и hashCode(o)
не обязательно возвращают одно и то же! Если вы делаете это для одного объекта, вам, вероятно, следует использовать hashCode
.
Ответ 2
По умолчанию hashCode() объекта возвращает адрес памяти для объекта. Так что, если у вас есть следующий класс:
class Car {
String make;
String model;
int year;
public Car(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
}
затем создайте два объекта:
Car car1 = new Car("Toyota", "Corolla", 2010);
Car car2 = new Car("Toyota", "Corolla", 2010);
car1.hashCode() будет отличаться от car2.hashCode(), потому что у каждого объекта будет свой адрес памяти.
Что если вы хотите, чтобы car1 и car2 возвращали один и тот же хэш-код? В этом случае вы должны переопределить метод hashCode() объекта по умолчанию для класса Car следующим образом:
@Override
public int hashCode() {
Object[] x = {model, make, Integer.valueOf(year)};
int hashArray = Arrays.hashCode(x);
return hashArray;
}
Это сделает car1.hashCode() равным car2.hashCode(), потому что String.hashCode() вычисляет hashCode на основе содержимого строки, а Integer.hashCode() возвращает само целочисленное значение.
В Java 7 вы можете просто использовать Objects.hash(Object... values). Итак, наш новый Car hashCode() будет выглядеть следующим образом:
@Override
public int hashCode() {
return Objects.hash(model, make, year);
}
Objects.hash(Object... values) вызовет Arrays.hashCode для вас.
Наконец, Objects.hashCode(Object o) выполнит нулевую проверку. Он вернет 0, если объект нулевой. Иначе, он вызовет метод hashCode() объектов.
Ответ 3
Objects.hashCode
Objects.hashCode( Object o )
метод Objects.hashCode( Object o )
просто вызывает метод hashCode
для переданного объекта.
Терпит NULL
Так зачем изобретать или использовать этот метод? Почему бы просто не вызвать метод hashCode
объектов самостоятельно?
Этот метод предлагает одно преимущество: NULL
➙ 0
. Служебный метод допускает нулевое значение.
- Если вы вызываете
Objects.hashCode( myObject )
где myObject
равен NULL
, вы получаете ноль (0). - Напротив, вызов
myObject.hashCode()
когда myObject
равен NULL
вызывает аргумент NullPointerException
.
Желательно ли терпеть ноль или нет, зависит от вашего собственного суждения в вашей конкретной ситуации.
Objects.hash
Objects.hash( Object o, … )
метод Objects.hash( Object o, … )
служит для другой цели. Этот метод имеет две фазы:
- Вызовите
.hashCode
для каждого переданного объекта, собирая каждый результат. - Рассчитайте хеш по собранным результатам.
хеш хеш
Если вы передаете один объект Objects.hash( myObject )
, сначала вызывается и собирается myObject.hashCode
, а затем вычисляется хэш для этой коллекции из одного элемента. Итак, вы получите хеш хеша.
При хешировании одного объекта важно понимать, что Objects.hashCode( myObject )
возвращает результат, отличный от Objects.hash( myObject )
. По сути, второе возвращает хеш по результату первого.
Раздражает на практике
Логика подхода, принятого в этих двух методах Objects
, сама по себе имеет смысл.
К сожалению, на практике для тех из нас, кто пытается использовать их в повседневной работе при написании кода на наших объектах POJO для переопределения hashCode
и, соответственно, equals
, мы должны дважды подумать, чтобы решить, какой из них вызывать.
- Если ваше переопределение
hashCode
(и equals
) основано на одном члене вашего класса, используйте Objects.hashCode( member )
. - Если ваше переопределение
hashCode
(и equals
) основано на нескольких атрибутах вашего класса, используйте Objects.hash( memberA, memberB, memberC )
.
Единственный член, не терпящий NULL
@Override
public int hashCode() {
return this.member.hashCode() ; // Throws NullPointerException if member variable is null.
}
Единственный член, допускающий значение NULL
@Override
public int hashCode() {
return Objects.hashCode( this.member ) ; // Returns zero (0) if 'this.member' is NULL, rather than throwing exception.
}
Multi-член, терпящий NULL
@Override
public int hashCode() {
return Objects.hash( this.memberA , this.memberB , this.memberC ) ; // Hashes the result of all the passed objects individual hash codes.
}
пример
Мы можем протестировать эти различные методы довольно просто.
UUID
Давайте использовать объект UUID
в качестве примера. UUID (универсальный уникальный идентификатор) - это 128-битное значение, в котором определенные биты имеют определенную семантику.
Реализация UUID
OpenJDK внутренне представляет 128-битное значение в виде пары 64-битных long
целых чисел.
Та же самая реализация переопределяет Object::equals
и Object::hashCode
для просмотра данных, хранящихся в этой паре длинных целых чисел. Вот исходный код для этих двух методов.
public boolean equals(Object obj) {
if ((null == obj) || (obj.getClass() != UUID.class))
return false;
UUID id = (UUID)obj;
return (mostSigBits == id.mostSigBits &&
leastSigBits == id.leastSigBits);
}
public int hashCode() {
long hilo = mostSigBits ^ leastSigBits;
return ((int)(hilo >> 32)) ^ (int) hilo;
}
Пример кода
Создайте объект UUID.
UUID uuid = UUID.randomUUID();
Рассчитайте наши значения хеша.
int hash1 = uuid.hashCode();
int hash2 = Objects.hashCode( uuid ); // Result matches line above.
int hash3 = Objects.hash( uuid ); // Returns a hash of a hash.
int hash4 = Objects.hash( uuid.hashCode() ); // Result matches line above.
Дамп на консоль.
System.out.println( "uuid.toString(): " + uuid.toString() );
System.out.println( " 1/2 = " + hash1 + " | " + hash2 );
System.out.println( " 3/4 = " + hash3 + " | " + hash4 );
Смотрите этот код в прямом эфире на IdeOne.com.
uuid.toString(): 401d88ff-c75d-4607-bb89-1f7a2c6963e1
1/2 = 278966883 | 278966883
3/4 = 278966914 | 278966914