2008-09-24 28 views
5

Trong Java, tôi có một lớp con Vertex của lớp Java3D Point3f. Bây giờ Point3f tính equals() dựa trên giá trị tọa độ của nó, nhưng đối với lớp Vertex của tôi, tôi muốn được chặt chẽ hơn: hai đỉnh chỉ bằng nhau nếu chúng là cùng một đối tượng. Cho đến nay, như vậy tốt:Làm thế nào để tính toán hashCode() từ địa chỉ của đối tượng?

class Vertex extends Point3f { 

    // ... 

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

Tôi biết điều này là vi phạm hợp đồng của equals(), nhưng kể từ khi tôi sẽ chỉ so sánh đỉnh đến đỉnh khác đây không phải là một vấn đề.

Bây giờ, để có thể đặt đỉnh vào một HashMap, phương pháp hashCode() phải trả lại kết quả phù hợp với equals(). Nó hiện đang làm điều đó, nhưng có lẽ căn cứ giá trị trả về của nó trên các lĩnh vực của Point3f, và do đó sẽ cung cấp cho va chạm băm cho các đối tượng khác nhau Vertex với cùng tọa độ.

Vì vậy, tôi muốn căn cứ số hashCode() trên địa chỉ của đối tượng, thay vì tính toán nó từ các trường của Vertex. Tôi biết rằng lớp Object thực hiện điều này, nhưng tôi không thể gọi phương thức hashCode()Point3f ghi đè nó.

Vì vậy, thực sự câu hỏi của tôi có hai phần:

  • nên tôi thậm chí còn muốn như vậy một nông equals()?
  • Nếu có, thì làm cách nào để tôi nhận được địa chỉ của đối tượng để tính toán mã băm từ?

Chỉnh sửa: Tôi chỉ nghĩ về điều gì đó ... Tôi có thể tạo giá trị ngẫu nhiên int để tạo đối tượng và sử dụng mã băm đó. Đó có phải là một ý tưởng tốt? Tại sao không)?

Trả lời

10

Hoặc sử dụng System.identityHashCode() hoặc sử dụng IdentityHashMap.

+0

Thực ra ... đó chính xác là những gì tôi cần: D – Thomas

-2

Hàm hashCode() được kế thừa từ Object và hoạt động chính xác như bạn dự định (ở cấp đối tượng, không phải là cấp phối). Không cần phải thay đổi nó. Đối với phương pháp equals của bạn, không có lý do để sử dụng nó, vì bạn chỉ có thể làm obj1 == obj2 trong mã của bạn thay vì sử dụng bằng, vì nó có nghĩa là sắp xếp và tương tự, khi so sánh tọa độ tạo ra nhiều ý nghĩa hơn.

+0

Tôi không thể kiểm soát so sánh những gì HashMap sử dụng ... – Thomas

1

System.identityHashCode() trả lại cùng mã băm cho đối tượng đã cho như sẽ được trả về theo phương thức mặc định hashCode(), cho dù lớp của đối tượng đã cho ghi đè hashCode() hay không.

0

Bạn sử dụng một đại biểu mặc dù điều này answer có lẽ tốt hơn.


class Vertex extends Point3f{ 
    private final Object equalsDelegate = new Object(); 
    public boolean equals(Object vertex){ 
     if(vertex instanceof Vertex){ 
     return this.equalsDelegate.equals(((Vertex)vertex).equalsDelegate); 
     } 
     else{ 
     return super.equals(vertex); 
     } 
    } 
    public int hashCode(){ 
     return this.equalsDelegate.hashCode(); 
    } 
} 
0

Just FYI, bằng phương pháp của bạn không vi phạm hợp đồng bằng (đối với hợp đồng cơ sở Object rằng là) ... đó là về cơ bản bằng phương pháp đối với phương pháp cơ sở Object, vì vậy nếu bạn muốn tính bằng thay vì của Vertex bằng, đó là tốt. Đối với mã băm, bạn thực sự không cần phải thay đổi nó, mặc dù câu trả lời được chấp nhận là một lựa chọn tốt và sẽ hiệu quả hơn nhiều nếu bảng băm của bạn chứa nhiều khóa đỉnh có cùng giá trị .

Lý do bạn không cần phải thay đổi nó là vì hoàn toàn tốt khi mã băm sẽ trả lại cùng giá trị cho các đối tượng bằng trả về false ... nó thậm chí còn là mã băm hợp lệ để trả về 0 tất cả thời gian cho MỌI ví dụ. Cho dù điều này có hiệu quả đối với các bảng băm là vấn đề hoàn toàn khác nhau ... bạn sẽ nhận được nhiều xung đột hơn nếu nhiều đối tượng của bạn có cùng mã băm (có thể là trường hợp nếu bạn để mã băm một mình và có nhiều đỉnh với cùng giá trị).

Xin đừng chấp nhận điều này là câu trả lời mặc dù tất nhiên (những gì bạn đã chọn nhiều hơn thực tế), tôi chỉ muốn cung cấp cho bạn một chút thông tin nền thêm về mã băm và bằng ;-)

+0

Nó vi phạm hợp đồng, vì Point3f.bằng (Vertex) có thể trả về true, trong khi cuộc gọi ngược lại sẽ luôn trả về false. Do đó, việc triển khai của tôi không đối xứng. – Thomas

+0

Tôi cũng nhận ra rằng hashCode mặc định() là hợp lệ, nhưng nó đang làm nhiều hơn mức cần thiết. Và kể từ khi tôi sử dụng HashTable và HashSet khá nhiều, tôi đã tìm ra điều này sẽ là một chiến thắng hiệu suất. – Thomas

+0

tốt, nó vi phạm Point3f bằng phương pháp sau đó, không phải là đối tượng bằng phương pháp ... đó là những gì tôi đã đề cập đến (và những gì bảng băm quan tâm) –

0

Tại sao bạn có muốn ghi đè hashCode() ở địa điểm đầu tiên không? Bạn muốn làm điều đó nếu bạn muốn làm việc với một số định nghĩa bình đẳng khác. Ví dụ:

lớp công khai A { int id;

bình đẳng public boolean (A khác) {return other.id == id} public int hashCode() {return id;}

} nơi bạn muốn được rõ ràng rằng nếu id đều giống nhau sau đó các đối tượng giống nhau và bạn ghi đè mã băm để bạn không thể thực hiện việc này:

HashSet hash = new HashSet(); hash.add (mới A (1)); hash.add (mới A (1)); và nhận được 2 giống hệt nhau (từ quan điểm của định nghĩa bình đẳng của bạn) A's. Hành vi chính xác sẽ là bạn chỉ có 1 đối tượng trong băm, ghi thứ hai sẽ ghi đè lên.

+0

Những gì bạn nói là chính xác, nhưng tôi không thấy quan điểm của bạn. – Thomas

0

Vì bạn không sử dụng bằng so sánh lôgic, nhưng đối tượng vật lý (nghĩa là đối tượng đó), cách duy nhất bạn đảm bảo rằng mã băm sẽ trả về giá trị duy nhất, là triển khai biến thể của gợi ý riêng. Thay vì tạo một số ngẫu nhiên, hãy sử dụng UUID để tạo ra một giá trị duy nhất thực tế cho từng đối tượng.

Các System.identityHashCode() sẽ làm việc, nhất hết thời gian, nhưng không đảm bảo như các phương pháp Object.hashCode() là không đảm bảo để trả về một giá trị duy nhất cho mọi đối tượng. Tôi đã nhìn thấy trường hợp cận biên xảy ra, và nó có thể sẽ phụ thuộc vào việc thực hiện VM, mà không phải là một cái gì đó bạn sẽ muốn mã của bạn phụ thuộc vào.

Trích từ javadocs cho Object.hashCode(): Nhiều như là hợp lý thực tế, phương thức hashCode được xác định bởi lớp Object 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ỉ nội bộ 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 yêu cầu bởi ngôn ngữ lập trình JavaTM.)

Vấn đề này, là trường hợp có hai đối tượng điểm riêng biệt từ ghi đè lên nhau khi được chèn vào hashmap vì cả hai đều có cùng một giá trị băm. Vì không có logic bằng, với việc ghi đè đi kèm hashCode(), phương thức identityHashCode thực sự có thể khiến kịch bản này xảy ra. Trường hợp trường hợp logic sẽ chỉ thay thế các mục nhập băm cho cùng một điểm logic, sử dụng băm hệ thống có thể làm cho nó xảy ra với bất kỳ hai đối tượng nào, bình đẳng (và thậm chí cả lớp) không còn là một yếu tố nữa.

+0

System.identityHashCode() là tốt, bởi vì hashCode() không * dự kiến ​​* để trả về các giá trị riêng biệt cho các đối tượng riêng biệt; nó chỉ được yêu cầu trả về các giá trị bằng nhau cho các đối tượng bằng nhau. – Thomas

Các vấn đề liên quan