2.Thay đổi equals mà không thay đổi hashcode

Trong ví dụ trên, tại sao sau khi viết lại phương thức equals, sử dụng phương thức contrains của HashSet vẫn cho kết quả false. Đó là bởi vì bạn chỉ ghi đè lại phương thức equals mà không ghi đè phương thức hashCode.

Lưu ý rằng, các lớp trong collection thường lưu địa chỉ của các phần tử của nó bằng các mã băm(hash code) để giúp cho việc tìm kiếm hay sắp xếp nhanh hơn. Collection trong ví dụ trên là HashSet. Điều này có nghĩa rằng các thành phần của collection được đặt trong các đoạn băm(hash buckets) để xác định các giá trị hash code của chúng. Phương thức contains đầu tiên xác định một hash bucket để tìm kiếm và sau đó so sánh với các phần tử đã cho với tất cả các phần tử trong bucket đó. Trong ví dụ trên, bạn đã ghi đè phương thức equals nhưng không ghi đè phương thức hashCode, cho nên hashCode vẫn chứa các giá trị được lưu trong lớp cha của nó là Object. Điều này dẫn đến các hash code của p1 và p2 khác nhau mặc dù các trường của nó có giá trị giống nhau. Các hash code khác nhau có nghĩa các hash bucket khác nhau trong tập hợp, điều này có nghĩa là p1 và p2 nằm trong các bucket khác nhau và cho kết quả false.

Như vậy, vấn đề ở đây là việc thực hiện cuối cùng của Point vi pham quy tắc(contact) hashCode được định nghĩa trong Object:

Nếu hai đối tượng được gọi là bằng nhau theo phương thức equals, sau đó gọi phương thức hashCode trên mỗi đối tượng đó đều phải trả về kết quả cùng một số nguyên.

Như vậy, ta xây dựng lại lớp Point như sau:

public class Point {

    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override 
    public boolean equals(Object other) {
        boolean result = false;
        if (other instanceof Point) {
            Point that = (Point) other;
            result = (this.getX() == that.getX() && this.getY()       == that.getY());
        }
        return result;
    }

    @Override 
    public int hashCode() {
        return (41 * (41 + getX()) + getY());
    }
}

Trong ví dụ trên, giá trị trả về trong phương thức equals được tính bằng

41*(41 + getX() + getY());

Bây giờ, bạn thử gọi lại phương thức contains xem nhé:

Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);

HashSet<Point> coll = new HashSet<Point>();
coll.add(p1);

System.out.println(coll.contains(p2)); // prints true

Bạn thấy bài viết này thế nào?

Các bài liên quan:
Kỹ thuật lập trình hướng đối tượng – Phần 2