2012-03-10 44 views
16

Tôi gặp sự cố khi viết phương thức hashCode() cho lớp tôi đã tạo. Lớp này có nghĩa là được sử dụng bên trong một TreeSet, và như vậy, nó thực hiện Comparable. Lớp học có các biến sau:Tạo phương thức hashCode() - Java

public class Node implements Comparable<Node> { 
    Matrix matrix; 
    int[] coordinates= new int[2]; 
    Node father; 
    int depth; 
    int cost; 

Đây là cách triển khai phương pháp compareTo(). Tôi muốn các TreeSet để tổ chức các cấu trúc nút bằng chi phí của họ, do đó, compareTo() trả về kết quả của phép trừ đơn giản.

public int compareTo(Node nodeToCompare) { 
    return this.cost - nodeToCompare.cost; 
} 

Tôi cũng đã triển khai phương thức equals().

public boolean equals(Object objectToCompare) { 
    if(objectToCompare== this) {return true;} 
    if(objectToCompare== null || objectToCompare.getClass()!= this.getClass()) {return false;} 

    Node objectNode= (Node) objectToCompare; 
    return this.father.equals(objectNode.father) && 
      this.depth== objectNode.depth && 
      this.cost== objectNode.cost && 
      this.matrix.equals(objectNode.matrix) && 
      Arrays.equals(this.coordinates, objectNode.coordinates); 
} 

Có nói tất cả điều đó, tôi có một vài câu hỏi:

  1. Kể từ khi tôi thực hiện một phương pháp mới equals(), tôi nên thực hiện một phương pháp mới hashCode()?
  2. Tôi làm cách nào để có thể triển khai mã băm mới method() với các biến đó? (Lưu ý rằng ma trận biến của loại Ma trận có phương pháp hashCode() được triển khai)

Đó là tất cả!

Trả lời

19

phương pháp compareTo của bạn không phù hợp với phương pháp equals của bạn: Phương pháp compareTo của bạn nói rằng hai trường hợp là tương đương nếu chúng có cùng cost — như vậy mà một TreeSet chỉ bao giờ có thể chứa ít nhất một ví dụ với một cost cho — nhưng bạn Phương pháp equals cho biết rằng chúng chỉ tương đương nếu chúng có cùng số cost giống nhau theo nhiều cách khác nhau.

Vì vậy, giả định rằng phương pháp equals của bạn là chính xác:

  • bạn cần phải sửa chữa phương pháp compareTo của bạn để phù hợp với nó.
  • bạn cần tạo phương thức hashCode phù hợp với phương pháp đó. Tôi khuyên bạn nên sử dụng cùng một loại logic như được sử dụng bởi java.util.List.hashCode(), đây là một cách đơn giản và hiệu quả để lắp ráp mã băm của các đối tượng thành phần theo thứ tự cụ thể; về cơ bản bạn sẽ viết một cái gì đó như:
    int hashCode = 1; 
    hashCode = 31 * hashCode + (father == null ? 0 : father.hashCode()); 
    hashCode = 31 * hashCode + depth; 
    hashCode = 31 * hashCode + cost; 
    hashCode = 31 * hashCode + matrix.hashCode(); 
    hashCode = 31 * hashCode + java.util.Arrays.hashCode(coordinates); 
    return hashCode;
+0

Nhưng nếu tôi thay đổi phương thức compareTo, điều đó sẽ không thay đổi cách mọi thứ được sắp xếp trong một TreeSet? –

+1

@ GonçaloLourenço: Có. Đó là quan điểm của tôi; ngay bây giờ, nếu 'n1.cost == n2.cost', thì' TreeSet' chứa 'n1' không thể chứa' n2'. Đó có thực sự là điều bạn muốn không? – ruakh

+0

Chắc chắn là không. Đoán tôi sẽ phải sử dụng cái gì khác thay vì TreeSet. Vì bạn là người đầu tiên trả lời câu hỏi của tôi, tôi sẽ đánh dấu câu trả lời của bạn là câu trả lời đúng. Cảm ơn vì sự giúp đỡ! –

7

Intellij IDEA có thể thực hiện điều này dưới dạng tính năng 'nhấp chuột phải'. Chỉ cần nhìn thấy nó được thực hiện một cách chính xác sẽ dạy cho bạn rất nhiều.

Và bạn nên ghi đè cả hai trong mọi trường hợp.

+2

tương tự trong Eclipse: Nguồn> Generate hashCode() và equals() ... –

+0

@ToKra trong trường hợp của tôi , nó đã thêm dòng này 'result = prime * result + getOuterType(). hashCode();', nhưng tôi chỉ muốn các trường có hashcode nhất quán (giữa các tải lại của ứng dụng), do đó, bằng cách bình luận dòng đó nó hoạt động đúng. Đó là một lớp học bên trong tho, đó là lý do tại sao dòng đó xuất hiện. –

3

Hợp đồng cho phương thức hashCode nói rằng nếu hai đối tượng bằng nhau, thì gọi hàm hashCode() sẽ cho bạn kết quả giống nhau. Điều ngược lại không nhất thiết phải đúng, tức là nếu hai hashCodes giống nhau thì các đối tượng không phải bằng nhau.

Nhìn vào phương thức equals của bạn (cần biến btw dịch), bạn có thể thêm hashCodes của tất cả các biến thành viên nội bộ cần bằng với phương thức equals của bạn để cho đúng. ví dụ.

public int hashCode() { 
    return this.matrix.hashCode() + 
      this.coordinates[0] + 
      this.coordinates[1] + 
      this.father.hashCode() + 
      this.depth + this.cost;    
} 

Giả định ở trên rằng ma trận và cha không bao giờ trống, bạn cần đảm bảo rằng bạn kiểm tra null nếu trường hợp đó không đúng.

Nếu bạn cảm thấy thích mạo hiểm hơn, bạn có thể nhân một vài điều trên với nguyên tố để đảm bảo bạn không nhận được va chạm hashCode cho dữ liệu khác nhau (điều này sẽ giúp cải thiện hiệu suất nếu bạn đang sử dụng lớp của bạn trong hashTables và hashMaps). Nếu bạn cần phải phục vụ cho null, phương pháp trên có thể được viết tốt hơn một chút như thế này:

public int hashCode() { 
    return ((this.matrix == null) ? 0 : this.matrix.hashCode()) + 
      17 * this.coordinates[0] + 
      this.coordinates[1] + 
      ((this.father == null) ? 0 : this.father.hashCode()) + 
      31 * this.depth + 19 * this.cost;    
} 
+0

Lưu ý rằng 'cha' là kiểu' Node', vì vậy nếu nó không bao giờ là 'null', thì cách tiếp cận của bạn sẽ dẫn đến việc đệ quy vô hạn và tràn ngăn xếp. – ruakh

+0

Vâng, tôi sợ điều đó. Đoán tôi sẽ phải thay đổi một vài thứ xung quanh. Cám ơn vì đã trả lời câu hỏi của tôi! –

+0

Đúng, do đó chúng ta cần giả định rằng a) hoặc là cha không có giá trị, hoặc b) không có đường tròn nào trong biểu đồ. Nếu cả hai điều trên không đúng, thì chúng ta nên loại trừ người cha khỏi tính toán hashCode. – ksymeon

0

Nếu bộ sưu tập của bạn nhỏ, bạn có thể trả về hằng số từ phương thức hashCode. Nó sử dụng để tìm nhanh. hashCodes giống như các hộp, giữ các phần tử. Quy tắc là:

  1. Các thành phần bằng nhau phải cùng một hộp (có cùng mã băm) - chắc chắn;
  2. Các phần tử không bằng nhau có thể giống nhau hoặc trong các hộp khác nhau.

Sau đó, bạn trả về hằng số, bạn tuân thủ 2 quy tắc này, nhưng nó có thể làm giảm đáng kể các danh sách nhỏ (vì JVM sẽ tìm kiếm trong tất cả các phần tử và không chỉ trong các thành phần trong cùng một hộp). Nhưng trở lại liên tục là cách tiếp cận xấu.

PS: Xin lỗi vì bài viết của tôi. Tiếng Anh không phải là ngôn ngữ mẹ đẻ của tôi.

PPS: thường là bạn phải thực hiện phương thức hashCode trong cùng một cách như equals (sử dụng cùng một nguyên tố)

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