JavaRush /Blog Java /Random-VI /Nghỉ giải lao #168. Tại sao ghi đè các phương thức bằng v...

Nghỉ giải lao #168. Tại sao ghi đè các phương thức bằng và mã băm trong Java?

Xuất bản trong nhóm

Tại sao ghi đè các phương thức bằng và mã băm trong Java?

Nguồn: Medium Bài viết này tập trung vào hai phương thức có liên quan chặt chẽ với nhau: Equals()hashcode() . Bạn sẽ tìm hiểu cách chúng tương tác với nhau và cách ghi đè chúng một cách chính xác. Nghỉ giải lao #168.  Tại sao ghi đè các phương thức bằng và mã băm trong Java?  - 1

Tại sao chúng ta ghi đè phương thức Equals()?

Trong Java, chúng ta không thể quá tải hành vi của các toán tử như == , += , -+ . Họ làm việc theo một quy trình nhất định. Ví dụ: hãy xem xét hoạt động của toán tử == .

Toán tử == hoạt động như thế nào?

Nó kiểm tra xem hai tham chiếu đang được so sánh có trỏ đến cùng một thể hiện trong bộ nhớ hay không. Toán tử == sẽ chỉ đánh giá là đúng nếu hai tham chiếu biểu thị cùng một thể hiện trong bộ nhớ. Chúng ta hãy xem mã mẫu:
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
Giả sử trong chương trình của bạn, bạn đã tạo hai đối tượng Person ở những vị trí khác nhau và muốn so sánh chúng.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
Từ góc độ kinh doanh, cả hai đều giống nhau, phải không? Nhưng đối với JVM thì chúng không giống nhau. Vì cả hai đều được tạo bằng từ khóa new nên các phiên bản này nằm trong các phân đoạn bộ nhớ khác nhau. Do đó toán tử == sẽ trả về false . Nhưng nếu chúng ta không thể ghi đè toán tử == thì làm cách nào để nói với JVM rằng chúng ta muốn hai đối tượng này được đối xử như nhau? Đây là lúc phương thức .equals() phát huy tác dụng . Bạn có thể ghi đè bằng() để kiểm tra xem một số đối tượng có cùng giá trị cho một số trường nhất định hay không để coi chúng bằng nhau. Bạn có thể chọn trường để so sánh. Nếu chúng ta nói rằng hai đối tượng Person sẽ giống nhau nếu chúng có cùng tuổi và cùng tên, thì IDE sẽ tạo ra thứ gì đó như thế này để tự động tạo bằng() :
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
Hãy quay lại ví dụ trước của chúng tôi.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
Có, chúng ta không thể nạp chồng toán tử == để so sánh các đối tượng theo cách chúng ta muốn, nhưng Java cung cấp cho chúng ta một cách khác - phương thức Equals() mà chúng ta có thể ghi đè theo ý muốn. Hãy nhớ rằng nếu chúng ta không cung cấp phiên bản tùy chỉnh của .equals() (còn được gọi là ghi đè) trong lớp của mình thì .equals() được xác định trước từ lớp Object và toán tử == sẽ hoạt động giống nhau. Phương thức defaults () mặc định , được kế thừa từ Object , sẽ kiểm tra xem cả hai phiên bản được so sánh có giống nhau trong bộ nhớ hay không!

Tại sao chúng ta ghi đè phương thức hashCode()?

Một số cấu trúc dữ liệu trong Java, chẳng hạn như HashSetHashMap , lưu trữ các phần tử của chúng dựa trên hàm băm được áp dụng cho các phần tử đó. Hàm băm là hashCode() . Nếu chúng ta có quyền lựa chọn ghi đè phương thức .equals() thì chúng ta cũng nên có lựa chọn ghi đè phương thức hashCode() . Có lý do cho việc này. Xét cho cùng, việc triển khai mặc định của hashCode() , được kế thừa từ Object , coi tất cả các đối tượng trong bộ nhớ là duy nhất! Nhưng hãy quay lại với các cấu trúc dữ liệu băm này. Có một quy tắc cho các cấu trúc dữ liệu này. HashSet không thể chứa các giá trị trùng lặp và HashMap không thể chứa các khóa trùng lặp. HashSet được triển khai bằng cách sử dụng HashMap theo cách mà mỗi giá trị HashSet được lưu trữ dưới dạng khóa trong HashMap . HashMap hoạt động như thế nào ? HashMap là một mảng gốc có nhiều phân đoạn. Mỗi phân đoạn có một danh sách liên kết ( linkedList ). Danh sách liên kết này lưu trữ các khóa của chúng tôi. HashMap tìm thấy LinkedList chính xác cho mỗi khóa bằng cách sử dụng phương thức hashCode() , sau đó lặp qua tất cả các phần tử của LinkedList đó và áp dụng phương thức Equals() cho từng phần tử đó để kiểm tra xem phần tử đó có được chứa ở đó hay không. Khóa trùng lặp không được phép. Nghỉ giải lao #168.  Tại sao ghi đè các phương thức bằng và mã băm trong Java?  - 2Khi chúng ta đặt thứ gì đó vào HashMap , khóa sẽ được lưu trữ trong một trong các danh sách được liên kết này. Danh sách liên kết nào mà khóa này sẽ được lưu trữ sẽ được hiển thị bằng kết quả của phương thức hashCode() cho khóa đó. Nghĩa là, nếu key1.hashCode() cho kết quả là 4, thì key1 đó sẽ được lưu trữ ở phân đoạn thứ 4 của mảng trong LinkedList hiện có ở đó . Theo mặc định, phương thức hashCode() trả về các kết quả khác nhau cho mỗi phiên bản. Nếu chúng ta có một giá trị mặc định bằng() hoạt động như == , coi tất cả các phiên bản trong bộ nhớ là các đối tượng khác nhau thì sẽ không có vấn đề gì. Như bạn có thể nhớ lại, trong ví dụ trước, chúng tôi đã nói rằng chúng tôi muốn các phiên bản Person được coi là bằng nhau nếu tuổi và tên của chúng giống nhau.
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
Bây giờ, hãy tạo một bản đồ để lưu trữ các phiên bản này dưới dạng khóa với một chuỗi cụ thể làm cặp giá trị.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Trong lớp Person , chúng ta chưa ghi đè phương thức hashCode nhưng chúng ta có một phương thức bằng được ghi đè . Vì hashCode mặc định cung cấp các kết quả khác nhau cho các phiên bản Java khác nhau của Person1.hashCode()Person2.hashCode() nên có nhiều khả năng nhận được các kết quả khác nhau. Bản đồ của chúng tôi có thể kết thúc với những người khác nhau trong các danh sách liên kết khác nhau. Nghỉ giải lao #168.  Tại sao ghi đè các phương thức bằng và mã băm trong Java?  - 3Điều này đi ngược lại logic của HashMap . Xét cho cùng, HashMap không thể có nhiều khóa giống hệt nhau! Vấn đề là hashCode() mặc định được kế thừa từ lớp Object là không đủ. Ngay cả sau khi chúng ta ghi đè phương thức Equals() của lớp Person . Đó là lý do tại sao chúng ta phải ghi đè phương thức hashCode() sau khi ghi đè phương thức bằng . Bây giờ chúng ta hãy sửa lỗi này. Chúng ta cần ghi đè phương thức hashCode() để nó tính đến các trường tương tự như Equals() , cụ thể là agename .
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
Trong phương thức hashCode() chúng ta đã sử dụng một giá trị đơn giản (bạn có thể sử dụng bất kỳ giá trị nào khác). Tuy nhiên, nên sử dụng số nguyên tố để tạo ra ít vấn đề hơn. Hãy thử lưu trữ lại các khóa này trong HashMap của chúng tôi :
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Person1.hashCode()Person2.hashCode() sẽ giống nhau. Giả sử chúng bằng 0. HashMap sẽ chuyển đến phân đoạn 0 và trong đó LinkedList sẽ lưu user1 làm khóa có giá trị “1”. Trong trường hợp thứ hai, khi HashMap quay lại nhóm 0 để lưu trữ khóa Person2 có giá trị “2”, nó sẽ thấy rằng một khóa khác tương đương với nó đã tồn tại ở đó. Bằng cách này, nó sẽ ghi đè lên khóa trước đó. Và chỉ có chìa khóa Person2 sẽ tồn tại trong HashMap của chúng tôi . Đây là cách chúng tôi tìm hiểu cách hoạt động của quy tắc HashMap , quy định rằng bạn không thể sử dụng nhiều khóa giống hệt nhau! Tuy nhiên, hãy nhớ rằng các phiên bản không bằng nhau có thể có cùng mã băm và các phiên bản bằng nhau phải trả về cùng một mã băm.Nghỉ giải lao #168.  Tại sao ghi đè các phương thức bằng và mã băm trong Java?  - 4
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION