Ghi đè các phương thức bằng() và hashCode() trong Java
Equals
và hashCode
là các phương thức cơ bản được khai báo trong lớp Object
và có trong các thư viện Java tiêu chuẩn. Phương thức này еquals()
được sử dụng để so sánh các đối tượng và hashCode
tạo mã số nguyên cho đối tượng. Các phương thức này được sử dụng rộng rãi trong các thư viện chuẩn Java khi chèn và truy xuất các đối tượng trong các tệp HashMap
. Phương pháp này equal
cũng được sử dụng để đảm bảo rằng chỉ các đối tượng duy nhất được lưu trữ trong các triển khai HashSet
khác Set
, cũng như trong bất kỳ trường hợp nào khác mà các đối tượng cần được so sánh. Việc triển khai mặc định của phương thức equals()
trong lớp java.lang.Object
so sánh các tham chiếu đến các địa chỉ bộ nhớ mà các biến lưu trữ và true
chỉ trả về nếu các địa chỉ khớp nhau, nói cách khác, các biến tham chiếu đến cùng một đối tượng. Java khuyên bạn nên ghi đè các phương thức equals()
and hashCode()
nếu bạn muốn việc so sánh được thực hiện theo logic tự nhiên hoặc logic nghiệp vụ. Nhiều lớp trong thư viện Java tiêu chuẩn ghi đè lên chúng, ví dụ lớp String
ghi đè equals
để nó trả về true
nếu nội dung của hai đối tượng được so sánh giống nhau. Lớp trình bao bọc ghi đè Integer
phương thức này equal
để thực hiện so sánh số, v.v. Vì Java HashMap
cũng dựa vào các phương thức and để so sánh and , nên Java đưa ra các quy tắc sau để ghi đè các phương thức này: HashTable
equals()
hashCode()
key
values
- Tính phản thân: Vật phải bằng chính nó.
- Đối xứng: nếu
a.equals(b)
nó trả vềtrue
, thìb.equals(a)
nó cũng phải trả vềtrue
. - Tính bắc cầu: nếu
a.equals(b)
nó trả vềtrue
vàb.equals(c)
cũng trả vềtrue
, thìc.equals(a)
nó cũng phải trả vềtrue
. - Tính nhất quán: Các lệnh gọi lặp lại đến một phương thức
equals()
phải trả về cùng một giá trị miễn là một số giá trị thuộc tính của đối tượng không bị thay đổi. Nghĩa là, nếu hai đối tượng bằng nhau trong Java thì chúng sẽ bằng nhau miễn là các thuộc tính của chúng không thay đổi. - So sánh
null
: Đối tượng phải được kiểm tranull
. Nếu đối tượng bằngnull
, thì phương thức sẽ trả vềfalse
, không phảiNullPointerException
. Ví dụ,a.equals(null)
nó sẽ trả vềfalse
.
Thỏa thuận giữa bằng và hashCode trong Java
- Nếu các đối tượng bằng nhau về kết quả thực hiện phương thức
equals
thì chúnghashcode
phải giống nhau. - Nếu các đối tượng không bằng nhau trong kết quả thực thi phương thức
equals
thì chúnghashcode
có thể giống nhau hoặc khác nhau. Tuy nhiên, để cải thiện hiệu suất, tốt hơn là để các đối tượng khác nhau trả về các mã khác nhau.
Cách ghi đè phương thức bằng trong Java
-
@Override public boolean equals(Object obj) { /*1. Check*/
if (obj == this) { /*and return */ return true; }
-
Проверьте an object на
null
, а также проверьте, чтобы an objectы были одного типа. Не делайте проверку с помощьюinstanceof
так How такая проверка будет возвращатьtrue
для подклассов и будет работать правильно только в случае если ваш класс объявлен Howimmutable
. Вместо этого можно использоватьgetClass()
;if (obj == null || obj.getClass() != this.getClass()) { return false; }
-
Объявите переменную типа, который вы сравниваете, и приведите
obj
к этому типу. Потом сравнивайте каждый атрибут типа начиная с численных атрибутов (если имеются) потому что численные атрибуты проверяются быстрей. Сравнивайте атрибуты с помощью операторов И и ИЛИ (так называемыеshort-circuit logical operators
) для объединения проверок с другими атрибутами.Person guest = (Person) obj; return id == guest.id && (firstName == guest.firstName || (firstName != null && firstName.equals(guest.getFirstName()))) && (lastName == guest.lastName || (lastName != null && lastName .equals(guest.getLastName()))); }
/** * Person class with equals and hashcode implementation in Java * @author Javin Paul */
public class Person {
private int id;
private String firstName;
private String lastName;
public int getId() { return id; }
public void setId(int id) { this.id = id;}
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
Person guest = (Person) obj;
return id == guest.id
&& (firstName == guest.firstName
|| (firstName != null &&firstName.equals(guest.getFirstName()))) && (lastName == guest.lastName
|| (lastName != null && lastName .equals(guest.getLastName())
));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); result = prime * result + id; result = prime * result +
((lastName == null) ? 0 : lastName.hashCode()); return result;
}
}
Распространенные ошибки при переопределении equals в Java
-
Вместо того, чтобы переопределять метод
equals (Override)
программист перегружает его(Overload)
Синтаксис методаequals()
в классеObject
определен Howpublic boolean equals(Object obj)
, но многие программисты ненароком перегружают метод:public boolean equals(Person obj)
- instead ofObject
в качестве аргумента используют Name своего класса (напр. Person). Эту ошибку сложно обнаружить из-заstatic binding
. Таким образом, если вы вызовете этот метод для an object своего класса, то метод не просто скомпorруется, а даже сделает это корректно. Однако, если вы положите ваш an object в коллекцию, напримерArrayList
и вызовете методcontains()
, работа которого основана на методеequals()
, то методcontains
не сможет обнаружить ваш an object. -
При переопределении метода
equals()
не проверять наnull
переменные, что в конечном итоге заканчиваетсяNullPointerException
при вызовеequals()
. Ниже представлен корректный code.firstname == guest.firstname || (firstname != null && firstname.equals(guest.firstname));
-
Третья распространенная ошибка это не переопределять метод
hashCode()
, а толькоequals()
. Вы обязаны переопределять оба методаequals()
иhashCode()
в Java. МетодhashCode
используется вhash
-коллекциях(напримерHashSet
), и чем меньше будет коллизий (одинаковый code при разных an objectх) тем эффективнее эти коллекции будут работать с an objectми вашего класса. -
Последняя распространенная ошибка программистов в том, что при переопределении метода
equals()
не сохраняется соответствие между методамиequals()
иcompareTo()
, что является неформальным требованием для избежания хранения дубликатов вSet (SortedSet, TreeSet)
.
Подсказки How писать в Java метод equals
-
Большинство IDE такие How NetBeans, Eclipse и IntelliJ IDEA обеспечивают поддержку генерации методов
equals()
иhashCode()
. В Eclipse нажмите правую кнопку -> source ->generate equals()
иhashCode()
. -
Если в классе есть уникальный бизнес-ключ, то будет достаточно сделать проверку только на equalsство этих полей. Как в нашем примере “id” - уникальный номер для каждого Person.
-
При переопределении
hashCode()
в Java удостоверьтесь в использовании всех полей, что были использованы в методеequals()
. -
String
và các lớp bao bọc nhưInteger
,Float
vàDouble
ghi đè phương thứcequals()
nhưngStringBuffer
không ghi đè. -
Bất cứ khi nào có thể, hãy tạo các trường
immutable
bằng cách sử dụngfinal
các biến trong Java. -
Khi so sánh
String
các đối tượng,equals()
thay vào đó hãy sử dụng toán tử==
. -
Hai đối tượng có giá trị logic bằng nhau nhưng được tải từ các đối tượng khác nhau
ClassLoader
thì không thể bằng nhau. Hãy nhớ rằng việc kiểm tra bằnggetClass()
sẽ trả vềfalse
nếu lớp trình tải khác. -
Cũng sử dụng
@Override
chú thích trên phương thức .hashCode
, vì điều này ngăn ngừa các lỗi tinh vi như giá trị trả về của .Tuyint
nhiên, một số lập trình viên trả vềlong
.
GO TO FULL VERSION