.Equals và .hashCode mặc định sẽ hoạt động như thế nào đối với các lớp của tôi?


106

Nói rằng tôi có lớp học của riêng mình

public class MyObj { /* ... */ }

Nó có một số thuộc tính và phương thức. Nó KHÔNG triển khai bằng, KHÔNG triển khai mã băm.

Khi chúng ta gọi bằng và Mã băm, các triển khai mặc định là gì? Từ lớp Đối tượng? Và họ là gì? Giá trị bằng mặc định sẽ hoạt động như thế nào? Mã băm mặc định sẽ hoạt động như thế nào và những gì sẽ trả về? == sẽ chỉ kiểm tra xem chúng có tham chiếu đến cùng một đối tượng hay không, vì vậy rất dễ dàng, nhưng còn các phương thức equals () và hashCode () thì sao?

Câu trả lời:


94

Có, triển khai mặc định là của Đối tượng (nói chung; nếu bạn kế thừa từ một lớp được xác định lại bằng và / hoặc Mã băm, thì bạn sẽ sử dụng triển khai đó thay thế).

Từ tài liệu:

equals

Phương thức bằng cho lớp Đối tượng thực hiện quan hệ tương đương có thể phân biệt nhất có thể trên các đối tượng; nghĩa là, đối với bất kỳ giá trị tham chiếu không rỗng nào x và y, phương thức này trả về true nếu và chỉ khi x và y tham chiếu đến cùng một đối tượng (x == y có giá trị true).

hashCode

Về mặt thực tế, phương thức hashCode được định nghĩa bởi class Object không trả về các số nguyên riêng biệt cho các đối tượng riêng biệt. (Điều này thường được thực hiện bằng cách chuyển đổi địa chỉ bên trong của đối tượng thành một số nguyên, nhưng kỹ thuật triển khai này không được ngôn ngữ lập trình JavaTM yêu cầu).


50

Từ Objectmột trong các triển khai JVM:

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

public int hashCode() {
    return VMMemoryManager.getIdentityHashCode(this);
}

Trong cả hai trường hợp, nó chỉ là so sánh địa chỉ bộ nhớ của các đối tượng được đề cập.


7
Phiên bản của JDK nó từ? Trong v6u23 ea:public native int hashCode();
khachik

@kha - Em nói đúng, tôi nghĩ rằng tôi theo dõi xuống một trong những triển khai bản địa để xem những gì nó thực sự đã làm
Brad Mace

10

Có các triển khai mặc định của equals()hashCode()trong Đối tượng. Nếu bạn không cung cấp cách triển khai của riêng mình, chúng sẽ được sử dụng. Đối với equals(), điều này có nghĩa là ==so sánh: các đối tượng sẽ chỉ bằng nhau nếu chúng chính xác là cùng một đối tượng. Đối với hashCode(), Javadoc có một lời giải thích tốt.

Để biết thêm thông tin, hãy xem Java hiệu quả, Chương 3 (pdf), mục 8.


1

Có, từ Objectlớp vì lớp của bạn mở rộng Đối tượng một cách ngầm định. equalschỉ đơn giản là trả lại this == obj. hashCodetriển khai là bản địa. Chỉ là phỏng đoán - nó trả về con trỏ cho đối tượng.


2
Nó là một con trỏ tới đối tượng nằm trong bộ nhớ, nhưng nó không phải là địa chỉ bộ nhớ của đối tượng. GC có thể di chuyển đối tượng xung quanh trong bộ nhớ và mã băm sẽ được giữ nguyên.
Jeremy

@Jeremy Cảm ơn. stackoverflow.com/questions/2427631/… có thể thú vị.
khachik

1

Nếu bạn không cung cấp cách triển khai của riêng mình, thì một phương thức bắt nguồn từ Object sẽ được sử dụng. Không sao cả, trừ khi bạn định đặt các cá thể lớp của mình vào ví dụ như HashSet (bất kỳ tập hợp nào thực sự sử dụng hashCode ()) hoặc thứ gì đó cần kiểm tra tính bình đẳng của đối tượng (tức là phương thức chứa () của HashSet). Nếu không, nó sẽ hoạt động không chính xác, nếu đó là những gì bạn đang yêu cầu.

Khá dễ dàng để cung cấp việc triển khai các phương pháp này của riêng bạn nhờ HashCodeBuilderEqualsBuilder từ Apache Commons Lang .


(a) Tại sao bạn nói việc triển khai mặc định 'bằng' của lớp Đối tượng sẽ không hoạt động chính xác với HashSet? Điều đó mâu thuẫn với các câu trả lời khác trên trang này. (b) Cảm ơn các liên kết Commons Lang.
Basil Bourque

1
@Basil: Tôi không nghĩ điều đó mâu thuẫn. Tất nhiên việc triển khai mặc định sẽ hoạt động ... bằng cách nào đó, nhưng không phải theo cách bạn mong đợi. Đó là, vì equals () sử dụng bình đẳng tham chiếu, nếu không, hai đối tượng giống hệt nhau sẽ "khác nhau" theo cách triển khai mặc định. Kết quả là, bạn có thể có hai trường hợp khác nhau của cùng một thứ giống hệt nhau trong Bộ của bạn. Và sử dụng khá điển hình của Bộ là khi bạn muốn loại bỏ bản sao ...
Paweł Dyda

@ PawełDyda: Hành vi mặc định thường đúng đối với các loại có thể thay đổi. Nếu FooBarlà các tham chiếu đến hai trường hợp khác nhau của một kiểu có thể thay đổi, và tồn tại một phương thức (ví dụ SomeMutatingMethod) Foo.SomeMutatingMethod()không ảnh hưởng đến Barcùng một cách nó thực hiện Foo, thì sự khác biệt đó phải đủ để coi các đối tượng là không bằng nhau.
supercat

0

Các nhà phát triển của IBM cho biết:

Theo cách triển khai mặc định này, hai tham chiếu chỉ bằng nhau nếu chúng tham chiếu đến cùng một đối tượng. Tương tự, việc triển khai mặc định của hashCode () được cung cấp bởi Object được dẫn xuất bằng cách ánh xạ địa chỉ bộ nhớ của đối tượng thành một giá trị số nguyên.

Tuy nhiên, để chắc chắn về chi tiết triển khai chính xác cho phiên bản Java của một nhà cung cấp cụ thể, tốt nhất bạn nên xem như là nguồn (nếu nó có sẵn)

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.