Hashcode và Equals là các phương thức đã được định nghĩa trong lớp Object, là lớp cha của tất cả các lớp trong java. Chính vì thế nên tất cả các đối tượng java kế thừa triển khai mặc định của hai phương thức này.
Mục Lục
Tác dụng của phương thức hashcode() và equals()
Equals()
: dùng để xác minh sự bằng nhau của hai đối tượng, hai đối tượng bằng nhau khi và chỉ khi nó cùng tham chiếu đến một vị trí ô nhớ. Hầu hết các lớp trong java ghi đè lại phương thức này để tạo ra logic so sánh của riêng nó.
Hashcode()
: trả về một giá trị số nguyên duy nhất cho đối tượng trong thời gian chạy, giá trị số nguyên được lấy từ địa chỉ bộ nhớ của đối tượng trong Heap.
Quan hệ giữa hashcode() và equals()
Khi ghi đè lại phương thức equals() thì chúng ta cũng phải ghi đè lại phương thức hashcode(), bởi vì các đối tượng bằng nhau phải có mã băm (hashcode) bằng nhau
Hợp đồng tương quan:
- Internal consistency : giá trị của hashcode() chỉ thay đổi khi vì chỉ khi có một thuộc tính trong equals() thay đổi, trong cùng một lần chạy.
- Equals consistency : các đối tượng bằng nhau phải trả về cùng một mã băm.
- Collisions : các đối tượng không bằng nhau có thể có cùng một mã băm.
Ghi đè equals mà không ghi đè hashcode() có được không?
Theo dõi đoạn code bên dưới, ban đầu chưa ghi đè phương thức hashcode()
public class MainTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
System.out.println(e1.equals(e2));
System.out.println("e1 hash code: " + e1.hashCode());
System.out.println("e2 hash code: " + e2.hashCode());
}
@Data
private static class Employee {
private Integer id;
private String firstName;
private String lastName;
private String department;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(id, employee.id) &&
Objects.equals(firstName, employee.firstName) &&
Objects.equals(lastName, employee.lastName) &&
Objects.equals(department, employee.department);
}
}
}
Ta thấy ban đầu chưa ghi đè phương thức hashcode(), thì mặc dù khi so sánh e1 và e2 thì nó trả về giá trị true, nhưng cả hai lại trả về hai mã băm khác nhau, vi phạm tương quan hợp đồng giữa equals và hashcode.
public class MainTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
System.out.println(e1.equals(e2));
System.out.println("e1 hash code: " + e1.hashCode());
System.out.println("e2 hash code: " + e2.hashCode());
}
@Data
private static class Employee {
private Integer id;
private String firstName;
private String lastName;
private String department;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(id, employee.id) &&
Objects.equals(firstName, employee.firstName) &&
Objects.equals(lastName, employee.lastName) &&
Objects.equals(department, employee.department);
}
@Override
public int hashCode() {
return Objects.hash(id, firstName, lastName, department);
}
}
}
Sau khi ghi đè phương thức hashcode() thì lúc này hai đối tượng e1 và e2 đã có cùng giá trị hashcode, lúc này chúng đã bằng nhau.
EqualsBuilder và HashCodeBuilder
Apache common cung cấp hai lớp tiện ích là EqualsBuilder và HashCodeBuilder để tiện xử lý phương thức equals và hashcode.
public class MainTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
System.out.println(e1.equals(e2));
System.out.println("e1 hash code: " + e1.hashCode());
System.out.println("e2 hash code: " + e2.hashCode());
}
@Data
private static class Employee {
private Integer id;
private String firstName;
private String lastName;
private String department;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return new EqualsBuilder().append(getId(), employee.getId()).isEquals();
}
@Override
public int hashCode() {
final int PRIME = 31;
return new HashCodeBuilder(getId() % 2 == 0 ? getId() + 1 : getId(), PRIME).toHashCode();
}
}
}
Phương pháp xử lý tối ưu
- luôn sử dụng các trường giống nhau để tạo hashcode() và equal()
- equals() phải đảm bảo tính nhất quán, nếu nó không bị sửa đổi thì đối tượng phải luôn trả về cùng một giá trị hashcode()
- nếu chúng ta ghi đè lại equals thì cũng phải ghi đè lại hashcode()
- chỉ nên ghi đè lại các phương thức equals và hashcode nếu chúng ta cần xử lý logic so sánh, nếu không có thể dùng mặc định.
Xem thêm các bài viết liên quan dưới đây:
- Bộ nhớ Heap và bộ nhớ Stack trong Java
- Quy ước đặt tên trong Java (Naming convention)
- Pass by value, Pass by reference trong java
- Từ khóa trong java và chức năng của nó (keywords)
- Kiểu dữ liệu trong java và tác dụng của nó
- Hướng dẫn cài đặt JDK 8 trên windows
- Phân biệt JDK JRE và JVM trong Java
- Improve Java Performance: thủ thuật tối ưu [Phần 1]
- Crack Intellij IDEA Ultimate version 2022
- Distributed Lock with Hazelcast and Spring
- How to build Rate Limit with Hazelcast and Spring Boot
- Biết sử dụng git cherry-pick để làm việc hiệu quả hơn
- Git revert với Git reset hoạt động như thế nào?
- Git stash giúp bạn trở nên chuyên nghiệp như thế nào
- Câu chuyện phỏng vấn online mùa Covid
- Series tìm hiểu System Design
- Series tìm hiểu Hazelcast
- Series tìm hiểu lập trình java
- Series crack Intellij IDEA
- Series tìm hiểu Docker
- Series tìm hiểu Git
- Series tìm hiểu Kafka
- Series tìm hiểu ElasticSearch
- Series tìm hiểu Linux
- Series phỏng vấn kĩ sư phần mềm
- Series review sách