Tại sao tôi cần ghi đè các phương thức bằng và hashCode trong Java?


383

Gần đây tôi đọc qua Tài liệu Công trình dành cho nhà phát triển này .

Tài liệu là tất cả về định nghĩa hashCode()equals()hiệu quả và chính xác, tuy nhiên tôi không thể tìm ra lý do tại sao chúng ta cần ghi đè hai phương thức này.

Làm thế nào tôi có thể đưa ra quyết định để thực hiện các phương pháp này một cách hiệu quả?


4
Có hai bài viết tuyệt vời tại program.guide giải thích chính xác điều này: Khi nào tôi nên ghi đè bằng? Tại sao bạn phải luôn ghi đè hashCode khi ghi đè bằng . (Cảnh báo, câu trả lời được chấp nhận là thực sự sai.)
aioobe

Ghi đè trường hợp chỉ bằng: hai đối tượng giống nhau sẽ có mã băm khác nhau = cùng một đối tượng đi trong nhóm khác nhau (trùng lặp). Trường hợp ghi đè mã băm duy nhất: hai đối tượng giống nhau sẽ có cùng mã băm = cùng một đối tượng đi trong cùng một nhóm (trùng lặp).
VdeX

Câu trả lời:


524

Joshua Bloch nói về Java hiệu quả

Bạn phải ghi đè hashCode () trong mỗi lớp ghi đè bằng (). Việc không làm như vậy sẽ dẫn đến vi phạm hợp đồng chung cho Object.hashCode (), điều này sẽ ngăn lớp của bạn hoạt động đúng kết hợp với tất cả các bộ sưu tập dựa trên hàm băm, bao gồm HashMap, Hashset và Hashtable.

Chúng ta hãy cố gắng hiểu nó với một ví dụ về những gì sẽ xảy ra nếu chúng ta ghi đè equals()mà không ghi đè hashCode()và cố gắng sử dụng một Map.

Giả sử chúng ta có một lớp như thế này và hai đối tượng MyClassbằng nhau nếu chúng importantFieldbằng nhau (với hashCode()equals()được tạo bởi nhật thực)

public class MyClass {

    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    public String getEqualField() {
        return importantField;
    }

    public String getAnotherField() {
        return anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }

}

Chỉ ghi đè equals

Nếu chỉ equalsbị overriden, thì khi bạn gọi myMap.put(first,someValue)đầu tiên sẽ băm vào một số nhóm và khi bạn gọi myMap.put(second,someOtherValue)nó sẽ băm vào một số nhóm khác (vì chúng có một nhóm khác hashCode). Vì vậy, mặc dù chúng bằng nhau, vì chúng không băm vào cùng một nhóm, bản đồ không thể nhận ra và cả hai đều ở trong bản đồ.


Mặc dù không cần ghi đè equals()nếu chúng ta ghi đè hashCode(), hãy xem điều gì sẽ xảy ra trong trường hợp cụ thể này khi chúng ta biết rằng hai đối tượng MyClassbằng nhau nếu chúng importantFieldbằng nhau nhưng chúng ta không ghi đè equals().

Chỉ ghi đè hashCode

Hãy tưởng tượng bạn có cái này

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

Nếu bạn chỉ ghi đè hashCodethì khi bạn gọi myMap.put(first,someValue)nó trước, hãy tính toán hashCodevà lưu nó trong một nhóm nhất định. Sau đó, khi bạn gọi myMap.put(second,someOtherValue)nó sẽ thay thế thứ nhất bằng thứ hai theo Tài liệu bản đồ vì chúng bằng nhau (theo yêu cầu nghiệp vụ).

Nhưng vấn đề là các đẳng thức không được xác định lại, vì vậy khi bản đồ băm secondvà lặp qua xô tìm xem có một vật thể knào đó second.equals(k)đúng như vậy không, nó sẽ không tìm thấy bất kỳ thứ gì như second.equals(first)vậy false.

Hy vọng nó đã rõ ràng


5
bạn có thể vui lòng giải thích thêm một chút, trong trường hợp thứ hai, tại sao đối tượng thứ hai phải đi trong một cái xô khác?
Hussain Akhtar Wahid 'Ghouri'

57
Tôi không thích câu trả lời này bởi vì nó gợi ý rằng bạn không thể ghi đè hashCode () mà không ghi đè bằng (), điều này đơn giản là không đúng. Bạn nói mã ví dụ của bạn (phần "chỉ ghi đè hashCode") sẽ không hoạt động vì bạn xác định hai đối tượng của mình là bằng nhau, nhưng - xin lỗi - định nghĩa này chỉ có trong đầu bạn. Trong ví dụ đầu tiên của bạn, bạn có hai đối tượng không bằng nhau có cùng mã băm và điều đó là hoàn toàn hợp pháp. Vì vậy, lý do bạn cần ghi đè bằng () không phải vì bạn đã ghi đè hashCode (), mà vì bạn muốn chuyển định nghĩa "bằng" của mình từ đầu sang mã.
dùng2543253

11
if you think you need to override one, then you need to override both of themsai. Bạn cần ghi đè hashCodenếu lớp của bạn ghi đè equalsnhưng ngược lại là không đúng.
akhil_mittal

4
Tôi nghĩ rằng hoàn toàn ổn khi chỉ ghi đè hashCode () mà không ghi đè bằng (). Đó cũng là những gì được viết bằng Java hiệu quả : Books.google.fr/ Kẻ
Johnny

2
@Ph PhantomReference, lưu ý rằng chỉ ghi đè equalssẽ vi phạm hợp đồng được đánh vần trong javadoc của Object: "Nếu hai đối tượng bằng nhau theo equals(Object)phương thức, thì việc gọi hashCodephương thức trên mỗi hai đối tượng phải tạo ra kết quả số nguyên giống nhau." Chắc chắn, không phải tất cả các phần của tất cả các hợp đồng đều được thực hiện trong tất cả các mã, nhưng vẫn nói chính thức đó là vi phạm và tôi coi đó là một lỗi đang chờ xảy ra.
aioobe

264

Các bộ sưu tập như HashMapHashSetsử dụng giá trị mã băm của một đối tượng để xác định cách lưu trữ bên trong bộ sưu tập và mã băm được sử dụng lại để xác định vị trí của đối tượng trong bộ sưu tập.

Lấy băm là một quá trình gồm hai bước:

  1. Tìm đúng xô (sử dụng hashCode())
  2. Tìm kiếm thùng cho đúng phần tử (sử dụng equals())

Đây là một ví dụ nhỏ về lý do tại sao chúng ta nên ghi đè equals()hashcode().

Hãy xem xét một Employeelớp học có hai lĩnh vực: tuổi và tên.

public class Employee {

    String name;
    int age;

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

Bây giờ tạo một lớp, chèn Employeeđối tượng vào a HashSetvà kiểm tra xem đối tượng đó có mặt hay không.

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

Nó sẽ in như sau:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

Bây giờ hashcode()phương thức uncomment , thực hiện tương tự và đầu ra sẽ là:

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

Bây giờ bạn có thể thấy tại sao nếu hai đối tượng được coi là bằng nhau, thì mã băm của chúng cũng phải bằng nhau không? Mặt khác, bạn sẽ không bao giờ có thể tìm thấy đối tượng vì phương thức mã băm mặc định trong lớp Đối tượng hầu như luôn luôn xuất hiện một số duy nhất cho mỗi đối tượng, ngay cả khi equals()phương thức được ghi đè theo cách hai hoặc nhiều đối tượng được coi là bằng nhau . Không quan trọng các đối tượng bằng nhau như thế nào nếu mã băm của họ không phản ánh điều đó. Vì vậy, một lần nữa: Nếu hai đối tượng bằng nhau, mã băm của chúng cũng phải bằng nhau.


4
Ví dụ hoàn hảo. Rõ ràng chứng minh sự khác biệt!
coderpc

3
giải thích tốt đẹp @rajeev
VdeX

2
@VikasVerma bằng đối tượng sẽ có mã băm bằng nhau không có nghĩa là đối tượng không bằng nhau sẽ có mã băm không bằng nhau. Điều gì xảy ra nếu các đối tượng thực sự khác nhau, nhưng mã băm của chúng giống nhau?
Ravi

1
giải thích rất hay :)
Rahul

4
Câu trả lời tốt hơn nhiều thì câu trả lời được chấp nhận! Cảm ơn
lũa

50

Bạn phải ghi đè hashCode () trong mỗi lớp ghi đè bằng (). Việc không làm như vậy sẽ dẫn đến vi phạm hợp đồng chung cho Object.hashCode (), điều này sẽ ngăn lớp của bạn hoạt động đúng kết hợp với tất cả các bộ sưu tập dựa trên hàm băm, bao gồm HashMap, Hashset và Hashtable.


   từ Java hiệu quả , bởi Joshua Bloch

Bằng cách xác định equals()hashCode()nhất quán, bạn có thể cải thiện khả năng sử dụng của các lớp của mình dưới dạng các khóa trong các bộ sưu tập dựa trên hàm băm. Như tài liệu API cho hashCode giải thích: "Phương pháp này được hỗ trợ vì lợi ích của các hashtables như được cung cấp bởi java.util.Hashtable."

Câu trả lời tốt nhất cho câu hỏi của bạn về cách triển khai các phương thức này một cách hiệu quả là đề nghị bạn đọc Chương 3 của Java hiệu quả .


4
Đây là câu trả lời đúng. Tất nhiên, tất nhiên, nếu bạn không bao giờ sử dụng lớp trong bộ sưu tập dựa trên hàm băm, thì bạn không thực hiện được vấn đề gì hashCode().
mỏng

1
Trong một trường hợp phức tạp hơn, bạn không bao giờ biết liệu các bộ sưu tập bạn sử dụng có sử dụng băm hay không, vì vậy hãy tránh xa "việc bạn chưa triển khai hashCode ()" là gì
Victor Sergienko

1
Tôi có thể ghi đè hashCode () mà không ghi đè bằng () không?
Johnny

@StasS, vâng, trái với những gì câu trả lời được chấp nhận nói. Xem giải thích trong phần thứ hai của bài viết này: Tại sao bạn phải luôn ghi đè
mã băm

22

Nói một cách đơn giản, phương thức đẳng thức trong Object kiểm tra sự bằng nhau tham chiếu, trong đó hai trường hợp của lớp bạn vẫn có thể bằng nhau về mặt ngữ nghĩa khi các thuộc tính bằng nhau. Ví dụ, điều này rất quan trọng khi đặt các đối tượng của bạn vào một thùng chứa sử dụng mã bằng và mã băm, như HashMapSet . Giả sử chúng ta có một lớp như:

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

Chúng tôi tạo hai trường hợp có cùng id :

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

Không ghi đè bằng chúng ta sẽ nhận được:

  • a.equals (b) là sai vì chúng là hai trường hợp khác nhau
  • a.equals (a) là đúng vì nó giống nhau
  • b.equals (b) là đúng vì nó là cùng một thể hiện

Chính xác? Có lẽ, nếu đây là những gì bạn muốn. Nhưng giả sử chúng ta muốn các đối tượng có cùng id là cùng một đối tượng, bất kể đó là hai trường hợp khác nhau. Chúng tôi ghi đè bằng (và mã băm):

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    }
}

Đối với việc triển khai bằng và mã băm, tôi có thể khuyên bạn nên sử dụng các phương thức trợ giúp của Guava


20

Bản sắc không bình đẳng.

  • bằng ==nhận dạng kiểm tra toán tử .
  • equals(Object obj) phương thức so sánh kiểm tra đẳng thức (tức là chúng ta cần nói về đẳng thức bằng cách ghi đè phương thức)

Tại sao tôi cần ghi đè các phương thức bằng và hashCode trong Java?

Đầu tiên chúng ta phải hiểu việc sử dụng phương thức bằng.

Để nhận dạng sự khác biệt giữa hai đối tượng, chúng ta cần ghi đè phương thức bằng.

Ví dụ:

Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.

------------------------------
Now I have overriden Customer class equals method as follows:
 @Override
    public boolean equals(Object obj) {
        if (this == obj)   // it checks references
            return true;
        if (obj == null) // checks null
            return false;
        if (getClass() != obj.getClass()) // both object are instances of same class or not
            return false;
        Customer other = (Customer) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference 
            return false;
        return true; 
    }
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2);  // returns true by our own logic

Bây giờ phương thức hashCode có thể hiểu dễ dàng.

hashCode tạo số nguyên để lưu trữ đối tượng trong các cấu trúc dữ liệu như HashMap , Hashset .

Giả sử chúng ta có phương thức ghi đè bằng Customernhư trên,

customer1.equals(customer2);  // returns true by our own logic

Trong khi làm việc với cấu trúc dữ liệu khi chúng ta lưu trữ đối tượng trong các thùng (xô là một tên ưa thích cho thư mục). Nếu chúng tôi sử dụng kỹ thuật băm tích hợp, đối với hai khách hàng ở trên, nó sẽ tạo ra hai mã băm khác nhau. Vì vậy, chúng tôi đang lưu trữ cùng một đối tượng ở hai nơi khác nhau. Để tránh loại vấn đề này, chúng ta nên ghi đè phương thức hashCode cũng dựa trên các nguyên tắc sau.

  • trường hợp không bằng nhau có thể có cùng mã băm.
  • trường hợp bằng nhau sẽ trả về cùng mã băm.

3
Đây là những gì tôi đã tìm kiếm từ 1 giờ qua. Người bạn đời tuyệt vời (y)
Adnan

13

Ok, hãy để tôi giải thích khái niệm bằng những từ rất đơn giản.

Đầu tiên từ góc độ rộng hơn, chúng tôi có các bộ sưu tập và hashmap là một trong những cơ sở hạ tầng trong các bộ sưu tập.

Để hiểu lý do tại sao chúng ta phải ghi đè cả phương thức bằng và mã băm, nếu trước tiên cần hiểu hashmap là gì và làm gì.

Hashmap là một cơ sở hạ tầng lưu trữ các cặp dữ liệu giá trị chính theo kiểu mảng. Hãy nói [], trong đó mỗi phần tử trong 'a' là một cặp giá trị chính.

Ngoài ra, mỗi chỉ mục trong mảng trên có thể được liên kết danh sách do đó có nhiều hơn một giá trị tại một chỉ mục.

Bây giờ tại sao một hashmap được sử dụng? Nếu chúng ta phải tìm kiếm trong một mảng lớn thì tìm kiếm qua từng mảng nếu chúng sẽ không hiệu quả, vì vậy kỹ thuật băm nào cho chúng ta biết rằng hãy xử lý trước mảng với một số logic và nhóm các thành phần dựa trên logic đó, ví dụ như Hashing

ví dụ: chúng ta có mảng 1,2,3,4,5,6,7,8,9,10,11 và chúng ta áp dụng hàm băm mod 10 nên 1,11 sẽ được nhóm lại với nhau. Vì vậy, nếu chúng ta phải tìm kiếm 11 trong mảng trước thì chúng ta sẽ phải lặp lại toàn bộ mảng nhưng khi chúng ta nhóm nó, chúng ta giới hạn phạm vi lặp của chúng ta do đó cải thiện tốc độ. Cơ sở hạ tầng được sử dụng để lưu trữ tất cả các thông tin trên có thể được coi là một mảng 2d cho đơn giản

Bây giờ ngoài hashmap ở trên cũng nói rằng nó sẽ không thêm bất kỳ Bản sao nào trong đó. Và đây là lý do chính tại sao chúng ta phải ghi đè bằng và mã băm

Vì vậy, khi nói rằng giải thích hoạt động nội bộ của hashmap, chúng ta cần tìm ra phương thức mà hashmap có và làm thế nào để tuân theo các quy tắc trên mà tôi đã giải thích ở trên

Vì vậy, hashmap có phương thức được gọi là put (K, V) và theo hashmap, nó phải tuân theo các quy tắc trên để phân phối hiệu quả mảng và không thêm bất kỳ bản sao nào

Vì vậy, điều cần làm là trước tiên nó sẽ tạo mã băm cho khóa đã cho để quyết định chỉ số nào sẽ có giá trị. Nếu không có gì ở chỉ mục đó thì giá trị mới sẽ được thêm vào đó, nếu có thứ gì đó hiện diện ở đó sau đó giá trị mới sẽ được thêm vào sau khi kết thúc danh sách được liên kết tại chỉ mục đó. nhưng hãy nhớ không nên thêm các bản sao theo hành vi mong muốn của hàm băm. vì vậy hãy nói rằng bạn có hai đối tượng Integer aa = 11, bb = 11. như mọi đối tượng xuất phát từ lớp đối tượng, việc thực hiện mặc định để so sánh hai đối tượng là nó so sánh tham chiếu và không phải các giá trị bên trong đối tượng. Vì vậy, trong trường hợp trên cả hai mặc dù bằng nhau về mặt ngữ nghĩa sẽ thất bại trong bài kiểm tra đẳng thức và khả năng hai đối tượng có cùng mã băm và cùng một giá trị sẽ tồn tại do đó tạo ra các bản sao. Nếu chúng ta ghi đè thì chúng ta có thể tránh thêm các mục trùng lặp. Bạn cũng có thể tham khảoChi tiết làm việc

import java.util.HashMap;


public class Employee {

String name;
String mobile;
public Employee(String name,String mobile) {
    this.name=name;
    this.mobile=mobile;
}

@Override
public int hashCode() {
    System.out.println("calling hascode method of Employee");
    String str=this.name;
    Integer sum=0;
    for(int i=0;i<str.length();i++){
        sum=sum+str.charAt(i);
    }
    return sum;

}
@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    System.out.println("calling equals method of Employee");
    Employee emp=(Employee)obj;
    if(this.mobile.equalsIgnoreCase(emp.mobile)){

        System.out.println("returning true");
        return true;
    }else{
        System.out.println("returning false");
        return false;
    }


}

public static void main(String[] args) {
    // TODO Auto-generated method stub

    Employee emp=new Employee("abc", "hhh");
    Employee emp2=new Employee("abc", "hhh");
    HashMap<Employee, Employee> h=new HashMap<>();
    //for (int i=0;i<5;i++){
        h.put(emp, emp);
        h.put(emp2, emp2);

    //}

    System.out.println("----------------");
    System.out.println("size of hashmap: "+h.size());


}

}

Tôi có một nhầm lẫn, tại sao chúng ta cần ghi đè phương thức bằng khi chúng ta ghi đè phương thức hashCode trong trường hợp HashMap? Trong mọi trường hợp, hashmap thay thế giá trị nếu mã băm của đối tượng bằng nhau.
Vikas Verma

Hashmap @VikasVerma không thay thế bất kỳ loại giá trị nào nếu mã băm của các đối tượng bằng nhau, nó chỉ quyết định chỉ mục nơi đặt đối tượng mới được thêm vào hashmap. Bây giờ có thể có các đối tượng tại chỉ mục, vì vậy để tránh trùng lặp, chúng ta ghi đè phương thức bằng và chúng ta viết logic để xác định khi hai đối tượng so sánh được coi là bằng nhau. Nếu không bị ghi đè thì mặc dù các đối tượng có cùng giá trị sẽ được lưu trữ vì tham chiếu của cả hai đối tượng sẽ khác nhau
Chetan

11

hashCode() :

Nếu bạn chỉ ghi đè phương thức mã băm thì sẽ không có gì xảy ra. Bởi vì nó luôn trở lại mớihashCode cho mỗi đối tượng dưới dạng một lớp Object.

equals() :

Nếu bạn chỉ ghi đè phương thức bằng nhau, a.equals(b)thì đúng, điều đó có nghĩa là hashCodea và b phải giống nhau nhưng không xảy ra. Bởi vì bạn đã không ghi đèhashCode phương thức.

Lưu ý: hashCode()phương thức của lớp Object luôn trả về new hashCodecho từng đối tượng.

Vì vậy, khi bạn cần sử dụng đối tượng của mình trong bộ sưu tập dựa trên băm, phải ghi đè cả hai equals()hashCode().


Đó là điểm thú vị, về việc ghi đè chỉ hashCode () . Nó hoàn toàn ổn, phải không? Hoặc có thể có trường hợp có vấn đề là tốt?
Johnny

1
Đây là một câu trả lời sai và sai. Ghi đè (= only =) hashCode () đảm bảo rằng mọi đối tượng đang được khởi tạo của lớp tương ứng có thuộc tính tương tự đều có cùng mã băm. Nhưng sẽ không hữu ích vì không ai trong số họ sẽ bằng nhau.
mfaisalhyder

8

Java đặt một quy tắc rằng

"Nếu hai đối tượng bằng nhau khi sử dụng lớp Object bằng phương thức, thì phương thức hashcode sẽ cho cùng một giá trị cho hai đối tượng này."

Vì vậy, nếu trong lớp chúng ta ghi đè, equals()chúng ta cũng nên ghi đè hashcode()phương thức để tuân theo quy tắc này. Cả hai phương thức, equals()hashcode(), được sử dụng trong Hashtable, ví dụ, để lưu trữ các giá trị dưới dạng các cặp khóa-giá trị. Nếu chúng ta ghi đè cái này chứ không phải cái kia, có khả năng cái đó Hashtablecó thể không hoạt động như chúng ta muốn, nếu chúng ta sử dụng đối tượng đó làm khóa.


6

Bởi vì nếu bạn không ghi đè lên chúng, bạn sẽ sử dụng hàm ý mặc định trong Object.

Cho rằng các giá trị đẳng thức và hascode thường đòi hỏi kiến ​​thức về những gì tạo nên một đối tượng mà chúng thường sẽ cần được xác định lại trong lớp của bạn để có bất kỳ ý nghĩa hữu hình nào.


6

Để sử dụng các đối tượng lớp riêng của chúng tôi làm khóa trong các bộ sưu tập như HashMap, Hashtable, v.v., chúng ta nên ghi đè cả hai phương thức (hashCode () và bằng ()) bằng cách nhận thức về hoạt động bên trong của bộ sưu tập. Mặt khác, nó dẫn đến kết quả sai mà chúng ta không mong đợi.


6

Thêm vào câu trả lời của @Lombo

Khi nào bạn sẽ cần ghi đè bằng ()?

Việc triển khai mặc định của Object's bằng () là

public boolean equals(Object obj) {
        return (this == obj);
}

có nghĩa là hai đối tượng sẽ chỉ được coi là bằng nhau nếu chúng có cùng địa chỉ bộ nhớ, điều này sẽ đúng chỉ khi bạn so sánh một đối tượng với chính nó.

Nhưng bạn có thể muốn xem xét hai đối tượng giống nhau nếu chúng có cùng giá trị cho một hoặc nhiều thuộc tính của chúng (Tham khảo ví dụ được đưa ra trong câu trả lời của @Lombo).

Vì vậy, bạn sẽ ghi đè equals()trong các tình huống này và bạn sẽ đưa ra các điều kiện của riêng bạn cho sự bình đẳng.

Tôi đã thực hiện thành công bằng () và nó đang hoạt động rất tốt. Vậy tại sao họ lại yêu cầu ghi đè hashCode ()?

Vâng, miễn là bạn không sử dụng Bộ sưu tập dựa trên "Hash" trên lớp do người dùng xác định, điều đó là ổn. Nhưng đôi khi trong tương lai bạn có thể muốn sử dụng HashMaphoặc HashSetnếu bạn không override"triển khai chính xác" hashCode () , các bộ sưu tập dựa trên Hash này sẽ không hoạt động như dự định.

Ghi đè chỉ bằng (Bổ sung cho câu trả lời của @Lombo)

myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

Trước hết, HashMap kiểm tra xem hashCode của secondcó giống như không first. Chỉ khi các giá trị là như nhau, nó sẽ tiến hành kiểm tra sự bằng nhau trong cùng một nhóm.

Nhưng ở đây, hashCode khác với 2 đối tượng này (vì chúng có địa chỉ bộ nhớ khác nhau - từ cách thực hiện mặc định). Do đó, nó thậm chí không quan tâm để kiểm tra sự bình đẳng.

Nếu bạn có một điểm dừng bên trong phương thức equals () bị ghi đè của mình, nó sẽ không bước vào nếu chúng có các mã băm khác nhau. contains()kiểm tra hashCode()và chỉ khi chúng giống nhau thì nó sẽ gọi equals()phương thức của bạn .

Tại sao chúng ta không thể thực hiện kiểm tra HashMap cho sự bình đẳng trong tất cả các nhóm? Vì vậy, không cần thiết cho tôi để ghi đè hashCode () !!

Sau đó, bạn đang thiếu điểm của Bộ sưu tập dựa trên Hash. Hãy xem xét những điều sau đây:

Your hashCode() implementation : intObject%9.

Sau đây là các khóa được lưu trữ dưới dạng xô.

Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...

Nói, bạn muốn biết bản đồ có chứa khóa 10. Bạn có muốn tìm kiếm tất cả các thùng không? hoặc bạn chỉ muốn tìm kiếm một thùng?

Dựa trên hashCode, bạn sẽ xác định rằng nếu có 10 thì nó phải có mặt trong Nhóm 1. Vì vậy, chỉ có Nhóm 1 sẽ được tìm kiếm !!


5
class A {
    int i;
    // Hashing Algorithm
    if even number return 0 else return 1
    // Equals Algorithm,
    if i = this.i return true else false
}
  • put ('key', 'value') sẽ tính toán giá trị băm bằng cách hashCode()xác định nhóm và sử dụng equals()phương thức để tìm xem giá trị đã có trong Nhóm hay chưa. Nếu không, nó sẽ được thêm vào, nó sẽ được thay thế bằng giá trị hiện tại
  • get ('key') sẽ sử dụng hashCode()để tìm Entry (xô) trước và equals()để tìm giá trị trong Entry

nếu cả hai bị ghi đè,

Bản đồ < Một >

Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...

nếu bằng không bị ghi đè

Bản đồ < Một >

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..

Nếu hashCode không bị ghi đè

Bản đồ < Một >

Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...

Hợp đồng bình đẳng HashCode

  1. Hai khóa bằng nhau theo phương thức bằng nhau sẽ tạo ra hashCode giống nhau
  2. Hai khóa tạo cùng mã băm không cần bằng nhau (Trong ví dụ trên, tất cả các số chẵn tạo cùng mã băm)

4

Xem xét bộ sưu tập các quả bóng trong một thùng tất cả các màu đen. Công việc của bạn là tô màu những quả bóng như sau và sử dụng nó cho trò chơi thích hợp,

Đối với Quần vợt - Vàng, Đỏ. Dành cho Cricket - Trắng

Bây giờ xô có bóng trong ba màu Vàng, Đỏ và Trắng. Và bây giờ bạn đã tô màu Chỉ có bạn biết màu nào dành cho trò chơi nào.

Tô màu các quả bóng - Băm. Chọn bóng cho trò chơi - Bằng.

Nếu bạn đã tô màu và một số người chọn bóng cho dế hoặc tennis, họ sẽ không quan tâm đến màu sắc !!!


4

Tôi đã xem xét lời giải thích "Nếu bạn chỉ ghi đè hashCode thì khi bạn gọi myMap.put(first,someValue)nó sẽ mất trước, hãy tính hashCode của nó và lưu nó vào một nhóm nhất định. Sau đó, khi bạn gọimyMap.put(first,someOtherValue) nó nên thay thế thứ hai bằng thứ hai theo Tài liệu bản đồ vì chúng bằng nhau (theo định nghĩa của chúng tôi). " :

Tôi nghĩ rằng lần thứ 2 khi chúng ta thêm vào myMapthì nó phải là đối tượng 'thứ hai' nhưmyMap.put(second,someOtherValue)


4

1) Lỗi phổ biến được thể hiện trong ví dụ dưới đây.

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

Không tìm thấy chiếc xe màu xanh lá cây

2. Sự cố do hashCode () gây ra

Vấn đề được gây ra bởi phương pháp không bị ghi đè hashCode(). Hợp đồng giữa equals()hashCode()là:

  1. Nếu hai đối tượng bằng nhau, thì chúng phải có cùng mã băm.
  2. Nếu hai đối tượng có cùng mã băm, chúng có thể hoặc không thể bằng nhau.

    public int hashCode(){  
      return this.color.hashCode(); 
    }

4

Nó rất hữu ích khi sử dụng Giá trị đối tượng . Sau đây là đoạn trích từ Kho lưu trữ mẫu Portland :

Ví dụ về các đối tượng giá trị là những thứ như số, ngày, tiền và chuỗi. Thông thường, chúng là những vật thể nhỏ được sử dụng khá rộng rãi. Danh tính của họ dựa trên trạng thái của họ chứ không phải dựa trên danh tính đối tượng của họ. Bằng cách này, bạn có thể có nhiều bản sao của cùng một đối tượng giá trị khái niệm.

Vì vậy, tôi có thể có nhiều bản sao của một đối tượng đại diện cho ngày 16 tháng 1 năm 1998. Bất kỳ bản sao nào trong số này sẽ bằng nhau. Đối với một đối tượng nhỏ như thế này, việc tạo ra những đối tượng mới và di chuyển chúng xung quanh sẽ dễ dàng hơn thay vì dựa vào một đối tượng duy nhất để thể hiện ngày.

Một đối tượng giá trị phải luôn ghi đè .equals () trong Java (hoặc = trong Smalltalk). (Hãy nhớ ghi đè .hashCode ().)


3

Giả sử bạn có lớp (A) tổng hợp hai (B) (C) khác và bạn cần lưu trữ các thể hiện của (A) bên trong hashtable. Việc thực hiện mặc định chỉ cho phép phân biệt các thể hiện, nhưng không phải bằng (B) và (C). Vì vậy, hai trường hợp của A có thể bằng nhau, nhưng mặc định sẽ không cho phép bạn so sánh chúng theo cách chính xác.


3

Các phương thức bằng và mã băm được định nghĩa trong lớp đối tượng. Theo mặc định, nếu phương thức bằng trả về true, thì hệ thống sẽ đi xa hơn và kiểm tra giá trị của mã băm. Nếu mã băm của 2 đối tượng cũng giống nhau thì các đối tượng sẽ được coi là giống nhau. Vì vậy, nếu bạn ghi đè chỉ bằng phương thức, thì mặc dù phương thức bằng ghi đè chỉ ra 2 đối tượng bằng nhau, mã băm do hệ thống xác định có thể không chỉ ra rằng 2 đối tượng bằng nhau. Vì vậy, chúng ta cần ghi đè mã băm là tốt.


Nếu phương thức bằng trả về true, không cần kiểm tra mã băm. Tuy nhiên, nếu hai đối tượng có mã băm khác nhau, thì người ta có thể coi chúng là khác nhau mà không phải gọi bằng. Hơn nữa, kiến ​​thức rằng không có thứ nào trong danh sách có mã băm cụ thể ngụ ý rằng không có thứ nào trong danh sách có thể khớp với đối tượng này với mã băm đó. Một ví dụ đơn giản, nếu một người có danh sách các đối tượng có mã băm là số chẵn và danh sách các đối tượng có số lẻ, thì không có đối tượng nào có mã băm là số chẵn sẽ nằm trong danh sách thứ hai.
supercat

Nếu một đối tượng có hai đối tượng X và Y có phương thức "bằng" cho biết chúng khớp với nhau, nhưng mã băm của X là số chẵn và mã băm của Y là một số lẻ, một bộ sưu tập như mô tả ở trên lưu ý rằng mã băm của đối tượng Y là số lẻ và được lưu trữ nó trong danh sách thứ hai sẽ không thể tìm thấy sự trùng khớp cho đối tượng X. Nó sẽ quan sát rằng mã băm của X là chẵn và vì danh sách thứ hai không có bất kỳ đối tượng nào có mã băm được đánh số chẵn, nên nó sẽ không làm phiền để tìm kiếm thứ gì đó phù hợp với X, mặc dù Y sẽ khớp với X. Bạn nên nói gì ...
supercat

... Sẽ là nhiều bộ sưu tập sẽ tránh so sánh những thứ mà mã băm sẽ ngụ ý rằng chúng không thể bằng nhau. Với hai đối tượng có mã băm là không rõ, nó thường là nhanh hơn để so sánh trực tiếp hơn tính toán mã băm của họ, vì vậy không có đảm bảo rằng mọi thứ mà báo cáo mã băm bất bình đẳng nhưng trở lại truecho equalssẽ không được coi là phù hợp. Mặt khác, nếu các bộ sưu tập xảy ra thông báo rằng mọi thứ không thể có cùng mã băm, thì có khả năng chúng không nhận thấy rằng chúng bằng nhau.
supercat

3

Các phương thức bằng và Hashcode trong Java

Chúng là các phương thức của lớp java.lang.Object, là siêu lớp của tất cả các lớp (các lớp tùy chỉnh cũng như các lớp khác được định nghĩa trong java API).

Thực hiện:

công bằng boolean bằng (Object obj)

công khai hàm băm ()

nhập mô tả hình ảnh ở đây

công bằng boolean bằng (Object obj)

Phương thức này chỉ đơn giản kiểm tra nếu hai tham chiếu đối tượng x và y tham chiếu đến cùng một đối tượng. tức là nó kiểm tra nếu x == y.

Đó là phản xạ: đối với bất kỳ giá trị tham chiếu x, x.equals (x) sẽ trả về true.

Nó là đối xứng: đối với mọi giá trị tham chiếu x và y, x.equals (y) sẽ trả về true khi và chỉ khi y.equals (x) trả về true.

Nó mang tính bắc cầu: bắc cầu đối với mọi giá trị tham chiếu x, y và z, nếu x.equals (y) trả về true và y.equals (z) trả về true, thì x.equals (z) sẽ trả về true.

Điều này phù hợp: đối với mọi giá trị tham chiếu x và y, nhiều cách gọi của x.equals (y) luôn trả về giá trị true hoặc trả về false, miễn là không có thông tin nào được sử dụng trong các phép so sánh trên đối tượng được sửa đổi.

Đối với mọi giá trị tham chiếu không null x, x.equals (null) sẽ trả về false.

công khai hàm băm ()

Phương thức này trả về giá trị mã băm cho đối tượng mà phương thức này được gọi. Phương thức này trả về giá trị mã băm dưới dạng một số nguyên và được hỗ trợ vì lợi ích của các lớp bộ sưu tập dựa trên băm như Hashtable, HashMap, Hashset, v.v. Phương thức này phải được ghi đè trong mỗi lớp ghi đè phương thức bằng.

Hợp đồng chung của hashCode là:

Bất cứ khi nào nó được gọi trên cùng một đối tượng nhiều lần trong khi thực thi ứng dụng Java, phương thức hashCode phải luôn trả về cùng một số nguyên, miễn là không có thông tin nào được sử dụng trong các phép so sánh trên đối tượng được sửa đổi.

Số nguyên này không cần nhất quán từ một thực thi của một ứng dụng đến một thực thi khác của cùng một ứng dụng.

Nếu hai đối tượng bằng nhau theo phương thức bằng (Object), thì việc gọi phương thức hashCode trên mỗi hai đối tượng phải tạo ra cùng một kết quả số nguyên.

Không yêu cầu rằng nếu hai đối tượng không bằng nhau theo phương thức bằng (java.lang.Object), thì việc gọi phương thức hashCode trên mỗi hai đối tượng phải tạo ra kết quả số nguyên riêng biệt. Tuy nhiên, lập trình viên cần lưu ý rằng việc tạo ra các kết quả số nguyên riêng biệt cho các đối tượng không bằng nhau có thể cải thiện hiệu suất của hashtables.

Các đối tượng bằng nhau phải tạo ra cùng một mã băm miễn là chúng bằng nhau, tuy nhiên các đối tượng không bằng nhau không cần tạo ra các mã băm riêng biệt.

Tài nguyên:

JavaRanch

Hình ảnh


Hình ảnh (liên kết video) ở chế độ riêng tư. Làm cho nó công khai để xem.
UdayKiran Pulipati

2

Trong ví dụ dưới đây, nếu bạn nhận xét ghi đè cho bằng hoặc mã băm trong lớp Người, mã này sẽ không tìm kiếm thứ tự của Tom. Sử dụng triển khai mặc định của mã băm có thể gây ra lỗi trong tra cứu hashtable.

Những gì tôi có dưới đây là một mã được đơn giản hóa để tăng thứ tự của mọi người theo Người. Person đang được sử dụng làm chìa khóa trong hashtable.

public class Person {
    String name;
    int age;
    String socialSecurityNumber;

    public Person(String name, int age, String socialSecurityNumber) {
        this.name = name;
        this.age = age;
        this.socialSecurityNumber = socialSecurityNumber;
    }

    @Override
    public boolean equals(Object p) {
        //Person is same if social security number is same

        if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
            return true;
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {        //I am using a hashing function in String.java instead of writing my own.
        return socialSecurityNumber.hashCode();
    }
}


public class Order {
    String[]  items;

    public void insertOrder(String[]  items)
    {
        this.items=items;
    }

}



import java.util.Hashtable;

public class Main {

    public static void main(String[] args) {

       Person p1=new Person("Tom",32,"548-56-4412");
        Person p2=new Person("Jerry",60,"456-74-4125");
        Person p3=new Person("Sherry",38,"418-55-1235");

        Order order1=new Order();
        order1.insertOrder(new String[]{"mouse","car charger"});

        Order order2=new Order();
        order2.insertOrder(new String[]{"Multi vitamin"});

        Order order3=new Order();
        order3.insertOrder(new String[]{"handbag", "iPod"});

        Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
        hashtable.put(p1,order1);
        hashtable.put(p2,order2);
        hashtable.put(p3,order3);

       //The line below will fail if Person class does not override hashCode()
       Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
        for(String item:tomOrder.items)
        {
            System.out.println(item);
        }
    }
}

2

Lớp chuỗi và lớp bao bọc có cách thực hiện khác nhau equals()hashCode() phương thức khác với lớp Object. Phương thức equals () của lớp Object so sánh các tham chiếu của các đối tượng, không phải nội dung. Phương thức hashCode () của lớp Object trả về mã băm riêng biệt cho mọi đối tượng cho dù nội dung có giống nhau hay không.

Nó dẫn đến sự cố khi bạn sử dụng bộ sưu tập Bản đồ và khóa là loại Liên tục, loại StringBuffer / trình xây dựng. Vì chúng không ghi đè bằng () và hashCode () không giống như lớp String, bằng () sẽ trả về false khi bạn so sánh hai đối tượng khác nhau mặc dù cả hai đều có cùng nội dung. Nó sẽ làm cho hashMap lưu trữ các khóa nội dung giống nhau. Lưu trữ các khóa nội dung giống nhau có nghĩa là nó vi phạm quy tắc của Bản đồ vì Bản đồ hoàn toàn không cho phép các khóa trùng lặp. Do đó, bạn ghi đè các phương thức equals () cũng như hashCode () trong lớp của bạn và cung cấp việc triển khai (IDE có thể tạo các phương thức này) để chúng hoạt động giống như String Equals () và hashCode () và ngăn chặn các khóa nội dung giống nhau.

Bạn phải ghi đè phương thức hashCode () cùng với equals () bởi vì equals () hoạt động theo mã băm.

Hơn nữa, ghi đè phương thức hashCode () cùng với equals () giúp giữ nguyên hợp đồng bằng () - hashCode (): "Nếu hai đối tượng bằng nhau, thì chúng phải có cùng mã băm."

Khi nào bạn cần viết triển khai tùy chỉnh cho hashCode ()?

Như chúng ta biết rằng hoạt động nội bộ của HashMap là trên nguyên tắc Hashing. Có một số thùng nhất định nơi các mục nhập được lưu trữ. Bạn tùy chỉnh triển khai hashCode () theo yêu cầu của bạn để các đối tượng thể loại tương tự có thể được lưu trữ vào cùng một chỉ mục. khi bạn lưu trữ các giá trị vào bộ sưu tập Bản đồ bằng put(k,v)phương thức, việc triển khai nội bộ của put () là:

put(k, v){
hash(k);
index=hash & (n-1);
}

Có nghĩa là, nó tạo ra chỉ mục và chỉ mục được tạo dựa trên mã băm của đối tượng khóa cụ thể. Vì vậy, làm cho phương thức này tạo mã băm theo yêu cầu của bạn bởi vì các mục nhập mã băm tương tự sẽ được lưu trữ vào cùng một nhóm hoặc chỉ mục.

Đó là nó!


1

hashCode()phương thức được sử dụng để lấy một số nguyên duy nhất cho đối tượng đã cho. Số nguyên này được sử dụng để xác định vị trí xô, khi đối tượng này cần được lưu trữ trong một số HashTable, HashMapnhư cấu trúc dữ liệu. Theo mặc định, hashCode()phương thức của Object trả về và biểu diễn số nguyên của địa chỉ bộ nhớ nơi đối tượng được lưu trữ.

Các hashCode()phương pháp của các đối tượng được sử dụng khi chúng ta chèn chúng vào một HashTable, HashMaphoặc HashSet. Thông tin thêm về HashTablesWikipedia.org để tham khảo.

Để chèn bất kỳ mục nào trong cấu trúc dữ liệu bản đồ, chúng ta cần cả khóa và giá trị. Nếu cả khóa và giá trị là người dùng xác định loại dữ liệu, hashCode()khóa sẽ được xác định nơi lưu trữ đối tượng bên trong. Khi yêu cầu tra cứu đối tượng từ bản đồ, mã băm của khóa sẽ được xác định nơi tìm kiếm đối tượng.

Mã băm chỉ trỏ đến một "khu vực" nhất định (hoặc danh sách, nhóm, v.v.) trong nội bộ. Do các đối tượng khóa khác nhau có khả năng có thể có cùng mã băm, nên chính mã băm không đảm bảo rằng khóa bên phải được tìm thấy. Sau HashTableđó lặp lại khu vực này (tất cả các khóa có cùng mã băm) và sử dụng equals()phương thức của khóa để tìm đúng khóa. Khi tìm thấy khóa bên phải, đối tượng được lưu trữ cho khóa đó được trả về.

Vì vậy, như chúng ta có thể thấy, sự kết hợp của các phương thức hashCode()equals()được sử dụng khi lưu trữ và khi tìm kiếm các đối tượng trong a HashTable.

GHI CHÚ:

  1. Luôn sử dụng các thuộc tính giống nhau của một đối tượng để tạo hashCode()equals()cả hai. Như trong trường hợp của chúng tôi, chúng tôi đã sử dụng id nhân viên.

  2. equals() phải nhất quán (nếu các đối tượng không được sửa đổi, thì nó phải tiếp tục trả về cùng một giá trị).

  3. Bất cứ khi nào a.equals(b), sau đó a.hashCode()phải giống như b.hashCode().

  4. Nếu bạn ghi đè lên một, thì bạn nên ghi đè lên cái khác.

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html


hashCode()không được sử dụng để trả về một số nguyên duy nhất cho mọi đối tượng. Đó là điều không thể. Bạn đã mâu thuẫn với chính mình trong câu thứ hai của đoạn thứ tư.
Hầu tước Lorne

@EJP, hầu hết các lần hascode () sẽ trả về interger duy nhất cho hai đối tượng khác nhau. Nhưng sẽ có cơ hội va chạm hascode cho hai đối tượng khác nhau, khái niệm này được gọi là Va chạm Hashcode . Vui lòng tham khảo: tech.queryhome.com/96931/ Mạnh
Paramesh Korrakuti

1

IMHO, theo quy tắc nói - Nếu hai đối tượng bằng nhau thì chúng phải có cùng hàm băm, nghĩa là các đối tượng bằng nhau sẽ tạo ra các giá trị băm bằng nhau.

Được đưa ra ở trên, mặc định bằng () trong Object là == không so sánh trên địa chỉ, hashCode () trả về địa chỉ theo số nguyên (hàm băm trên địa chỉ thực tế) một lần nữa khác biệt cho Object khác biệt.

Nếu bạn cần sử dụng các Đối tượng tùy chỉnh trong các bộ sưu tập dựa trên Hash, bạn cần ghi đè cả bằng () và hashCode (), ví dụ Nếu tôi muốn duy trì Hashset của Đối tượng nhân viên, nếu tôi không sử dụng hashCode mạnh hơn và bằng Tôi có thể kết thúc ghi đè hai Đối tượng nhân viên khác nhau, điều này xảy ra khi tôi sử dụng tuổi làm hashCode (), tuy nhiên tôi nên sử dụng giá trị duy nhất có thể là ID nhân viên.


1

Để giúp bạn kiểm tra các Đối tượng trùng lặp, chúng tôi cần một mã bằng và mã băm tùy chỉnh.

Vì mã băm luôn trả về một số luôn luôn nhanh để truy xuất một đối tượng bằng cách sử dụng một số thay vì khóa chữ cái. Nó sẽ làm như thế nào? Giả sử chúng ta đã tạo một đối tượng mới bằng cách chuyển một số giá trị đã có sẵn trong một số đối tượng khác. Bây giờ đối tượng mới sẽ trả về cùng giá trị băm như của đối tượng khác vì giá trị được truyền là như nhau. Khi trả về cùng một giá trị băm, JVM sẽ đi đến cùng một địa chỉ bộ nhớ và nếu trong trường hợp có nhiều hơn một đối tượng có cùng giá trị băm thì nó sẽ sử dụng phương thức equals () để xác định đúng đối tượng.


1

Khi bạn muốn lưu trữ và truy xuất đối tượng tùy chỉnh của mình dưới dạng khóa trong Bản đồ, thì bạn phải luôn ghi đè bằng và mã băm trong Đối tượng tùy chỉnh của mình. Ví dụ:

Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");

Ở đây p1 & p2 sẽ xem xét vì chỉ có một đối tượng và mapkích thước sẽ chỉ là 1 vì chúng bằng nhau.


1
public class Employee {

    private int empId;
    private String empName;

    public Employee(int empId, String empName) {
        super();
        this.empId = empId;
        this.empName = empName;
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + "]";
    }

    @Override
    public int hashCode() {
        return empId + empName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (!(this instanceof Employee)) {
            return false;
        }
        Employee emp = (Employee) obj;
        return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
    }

}

Lớp kiểm tra

public class Test {

    public static void main(String[] args) {
        Employee emp1 = new Employee(101,"Manash");
        Employee emp2 = new Employee(101,"Manash");
        Employee emp3 = new Employee(103,"Ranjan");
        System.out.println(emp1.hashCode());
        System.out.println(emp2.hashCode());
        System.out.println(emp1.equals(emp2));
        System.out.println(emp1.equals(emp3));
    }

}

Trong Object Class bằng (Object obj) được sử dụng để so sánh so sánh địa chỉ đó là lý do tại sao khi trong lớp Test nếu bạn so sánh hai đối tượng thì bằng phương thức cho sai nhưng khi chúng ta ghi đè mã băm () thì nó có thể so sánh nội dung và cho kết quả đúng.


và lớp Kiểm tra tôi đã thêm vào trong chương trình dưới đây.
Manash Ranjan Dakua 17/07/18

Trong Object Class bằng (Object obj) được sử dụng để so sánh so sánh địa chỉ đó là lý do tại sao khi trong lớp Test nếu bạn so sánh hai đối tượng thì bằng phương thức cho sai nhưng khi chúng ta ghi đè mã băm () thì nó có thể so sánh nội dung và cho kết quả đúng.
Manash Ranjan Dakua 17/07/18

1
bạn có thể sử dụng liên kết chỉnh sửa ngay bên dưới câu trả lời này để thêm vào câu trả lời của mình .. Vui lòng không thêm câu trả lời dưới dạng hai câu chưa hoàn chỉnh
Suraj Rao

1

Nếu bạn ghi đè equals()và không hashcode(), bạn sẽ không tìm thấy bất kỳ vấn đề nào trừ khi bạn hoặc ai đó sử dụng loại lớp đó trong bộ sưu tập băm như thế nào HashSet. Những người trước tôi đã giải thích rõ ràng lý thuyết tài liệu nhiều lần, tôi chỉ ở đây để cung cấp một ví dụ rất đơn giản.

Xem xét một lớp có equals()nhu cầu có nghĩa là một cái gì đó tùy chỉnh: -

    public class Rishav {

        private String rshv;

        public Rishav(String rshv) {
            this.rshv = rshv;
        }

        /**
        * @return the rshv
        */
        public String getRshv() {
            return rshv;
        }

        /**
        * @param rshv the rshv to set
        */
        public void setRshv(String rshv) {
            this.rshv = rshv;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Rishav) {
                obj = (Rishav) obj;
                if (this.rshv.equals(((Rishav) obj).getRshv())) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return rshv.hashCode();
        }

    }

Bây giờ hãy xem xét lớp chính này: -

    import java.util.HashSet;
    import java.util.Set;

    public class TestRishav {

        public static void main(String[] args) {
            Rishav rA = new Rishav("rishav");
            Rishav rB = new Rishav("rishav");
            System.out.println(rA.equals(rB));
            System.out.println("-----------------------------------");

            Set<Rishav> hashed = new HashSet<>();
            hashed.add(rA);
            System.out.println(hashed.contains(rB));
            System.out.println("-----------------------------------");

            hashed.add(rB);
            System.out.println(hashed.size());
        }

    }

Điều này sẽ mang lại đầu ra sau: -

    true
    -----------------------------------
    true
    -----------------------------------
    1

Tôi hài lòng với kết quả. Nhưng nếu tôi không bị ghi đè hashCode(), điều đó sẽ gây ra cơn ác mộng vì các đối tượng Rishavcó cùng nội dung thành viên sẽ không còn được coi là duy nhất như hashCodesẽ khác, như được tạo bởi hành vi mặc định, đây sẽ là đầu ra: -

    true
    -----------------------------------
    false
    -----------------------------------
    2

0

Cả hai phương thức được định nghĩa trong lớp Object. Và cả hai đều được thực hiện đơn giản nhất. Vì vậy, khi bạn cần bạn muốn thêm một số triển khai cho các phương thức này thì bạn đã ghi đè trong lớp của mình.

Đối với phương thức Ex: equals () trong đối tượng chỉ kiểm tra đẳng thức của nó trên tham chiếu. Vì vậy, nếu bạn cần so sánh trạng thái của nó thì bạn có thể ghi đè lên trạng thái đó như được thực hiện trong lớp String.


-3

Bah - "Bạn phải ghi đè hashCode () trong mỗi lớp ghi đè bằng ()."

[từ Java hiệu quả, bởi Joshua Bloch?]

Đây không phải là cách sai? Ghi đè mã băm có thể ngụ ý rằng bạn đang viết một lớp khóa băm, nhưng ghi đè bằng chắc chắn là không. Có nhiều lớp không được sử dụng làm khóa băm, nhưng muốn có một phương thức kiểm tra đẳng thức logic vì một số lý do khác. Nếu bạn chọn "bằng" cho nó, thì bạn có thể được ủy quyền viết một triển khai hashCode bằng cách áp dụng quá mức quy tắc này. Tất cả những gì đạt được là thêm mã chưa được kiểm tra vào cơ sở mã, một điều ác đang chờ đợi để làm vấp ngã ai đó trong tương lai. Ngoài ra viết mã bạn không cần là chống nhanh. Điều đó chỉ sai (và một ide được tạo có thể sẽ không tương thích với các công cụ thủ công của bạn).

Chắc chắn họ đã bắt buộc phải có Giao diện trên các đối tượng được viết để sử dụng làm khóa? Bất kể, Object không bao giờ nên cung cấp hàm hashCode () và bằng () imho. Có lẽ nó đã khuyến khích nhiều bộ sưu tập băm bị hỏng.

Nhưng dù sao, tôi nghĩ rằng "quy tắc" được viết trở lại phía trước. Trong thời gian này, tôi sẽ tiếp tục tránh sử dụng "bằng" cho các phương pháp kiểm tra đẳng thức :-(

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.