Làm thế nào để một phương thức ArrayList chứa () đánh giá các đối tượng?


303

Nói rằng tôi tạo một đối tượng và thêm nó vào ArrayList. Nếu sau đó tôi tạo một đối tượng khác với chính xác đầu vào của hàm tạo, contains()phương thức sẽ đánh giá hai đối tượng có giống nhau không? Giả sử hàm tạo không làm bất cứ điều gì buồn cười với đầu vào và các biến được lưu trữ trong cả hai đối tượng là giống hệt nhau.

ArrayList<Thing> basket = new ArrayList<Thing>();  
Thing thing = new Thing(100);  
basket.add(thing);  
Thing another = new Thing(100);  
basket.contains(another); // true or false?

class Thing {  
    public int value;  

    public Thing (int x) {
        value = x;
    }

    equals (Thing x) {
        if (x.value == value) return true;
        return false;
    }
}

Đây có phải là cách classthực hiện để có contains()lợi nhuận true?

Câu trả lời:


339

ArrayList implementsGiao diện danh sách.

Nếu bạn nhìn vào Javadoc choList vào containsphương pháp bạn sẽ thấy rằng nó sử dụng các equals()phương pháp để đánh giá nếu hai đối tượng đều giống nhau.


61
Chỉ trong trường hợp bạn có kế hoạch ghi đè bằng (), hãy đảm bảo bạn cũng ghi đè phương thức hashcode (). Nếu bạn không làm, mọi thứ có thể không hoạt động như mong đợi trong khi sử dụng Bộ sưu tập?
Mohd Farid

34
Đây là một câu trả lời đúng, nhưng lưu ý rằng bạn cần thay đổi phương thức bằng của mình để chấp nhận Objectthay vì a Thing. Nếu bạn không, phương thức bằng của bạn sẽ không được sử dụng. :)
mdierker

1
Bản thân tôi đã khám phá ra rằng nhật thực có "Tạo hashCode () và bằng" trong menu Nguồn.
Volodymyr Krupach

Điều này trả lời câu hỏi trong tiêu đề, nhưng không phải là câu hỏi trong phần mô tả, tức là "Nếu sau đó tôi tạo một đối tượng khác có cùng đầu vào hàm tạo, phương thức chứa () sẽ đánh giá hai đối tượng có giống nhau không?"
cướp

3
Collectionsthực hiện công cụ của họ theo cách tối ưu hóa, nghĩa là contains()trước tiên kiểm tra hashCodes của hai đối tượng và chỉ sau đó gọi equals(). Nếu các hashCodes là khác nhau (luôn luôn là trường hợp của hai trường hợp khác nhau Thing), equals()phương thức sẽ không được gọi. Theo nguyên tắc thông thường, khi bạn ghi đè equals(), bạn cũng không nên quên ghi đè hashCode().
Sevastyan Savanyuk

52

Tôi nghĩ rằng nên thực hiện đúng

public class Thing
{
    public int value;  

    public Thing (int x)
    {
        this.value = x;
    }

    @Override
    public boolean equals(Object object)
    {
        boolean sameSame = false;

        if (object != null && object instanceof Thing)
        {
            sameSame = this.value == ((Thing) object).value;
        }

        return sameSame;
    }
}

1
iftuyên bố là không cần thiết. instanceofLà đủ.
Paul

@Paul phần nào của tuyên bố bạn đang nói về?
ChristopheCVB

4
Điều object != nullkiện là không cần thiết, bởi vì object instanceof Thingkiểm tra đối tượng không phải là null quá.
Alexander Farber

15

ArrayList sử dụng phương thức equals được triển khai trong lớp (lớp Thing trường hợp của bạn) để thực hiện so sánh bằng.


12

Nói chung, bạn cũng nên ghi đè hashCode()mỗi lần bạn ghi đè equals(), ngay cả khi chỉ để tăng hiệu suất. HashCode()quyết định 'xô' đối tượng của bạn được sắp xếp khi thực hiện so sánh, do đó, bất kỳ hai đối tượng nào được equal()đánh giá là đúng sẽ trả về cùng một giá trị hashCode value(). Tôi không thể nhớ hành vi mặc định của hashCode()(nếu nó trả về 0 thì mã của bạn sẽ hoạt động nhưng chậm, nhưng nếu nó trả về địa chỉ thì mã của bạn sẽ thất bại). Tôi nhớ rất nhiều lần khi mã của tôi thất bại vì tôi quên ghi đè hashCode(). :)


7

Nó sử dụng phương thức bằng trên các đối tượng. Vì vậy, trừ khi Thing ghi đè bằng và sử dụng các biến được lưu trữ trong các đối tượng để so sánh, nó sẽ không trả về true trên contains()phương thức.


6
class Thing {  
    public int value;  

    public Thing (int x) {
        value = x;
    }

    equals (Thing x) {
        if (x.value == value) return true;
        return false;
    }
}

Bạn phải viết:

class Thing {  
    public int value;  

    public Thing (int x) {
        value = x;
    }

    public boolean equals (Object o) {
    Thing x = (Thing) o;
        if (x.value == value) return true;
        return false;
    }
}

Bây giờ nó hoạt động;)


6
bạn không nên làm điều x = (điều) o; mà không kiểm tra trước xem đối tượng kia có rỗng không
Steelshark

5

Chỉ muốn lưu ý rằng việc thực hiện sau đây là sai khi valuekhông phải là kiểu nguyên thủy:

public class Thing
{
    public Object value;  

    public Thing (Object x)
    {
        this.value = x;
    }

    @Override
    public boolean equals(Object object)
    {
        boolean sameSame = false;

        if (object != null && object instanceof Thing)
        {
            sameSame = this.value == ((Thing) object).value;
        }

        return sameSame;
    }
}

Trong trường hợp đó tôi đề xuất như sau:

public class Thing {
    public Object value;  

    public Thing (Object x) {
        value = x;
    }

    @Override
    public boolean equals(Object object) {

        if (object != null && object instanceof Thing) {
            Thing thing = (Thing) object;
            if (value == null) {
                return (thing.value == null);
            }
            else {
                return value.equals(thing.value);
            }
        }

        return false;
    }
}

Làm thế nào để thực hiện điều này trong khi loại bỏ trùng lặp?
Sujay

4

Các áp phích khác đã giải quyết câu hỏi về cách chứa () hoạt động.

Một khía cạnh quan trọng không kém của câu hỏi của bạn là làm thế nào để thực hiện đúng bằng (). Và câu trả lời cho điều này thực sự phụ thuộc vào những gì tạo nên sự bình đẳng đối tượng cho lớp đặc biệt này. Trong ví dụ bạn cung cấp, nếu bạn có hai đối tượng khác nhau mà cả hai đều có x = 5, chúng có bằng nhau không? Nó thực sự phụ thuộc vào những gì bạn đang cố gắng làm.

Nếu bạn chỉ quan tâm đến sự bình đẳng của đối tượng, thì việc triển khai mặc định .equals () (cái được cung cấp bởi Object) chỉ sử dụng danh tính (ví dụ: cái này == khác). Nếu đó là những gì bạn muốn, thì đừng thực hiện bằng () trên lớp của bạn (hãy để nó kế thừa từ Object). Mã bạn đã viết, trong khi loại chính xác nếu bạn đang nhận dạng, sẽ không bao giờ xuất hiện trong một lớp thực sự b / c, nó không mang lại lợi ích gì khi sử dụng triển khai Object.equals () mặc định.

Nếu bạn chỉ mới bắt đầu với công cụ này, tôi thực sự khuyên bạn nên đọc cuốn sách Java hiệu quả của Joshua Bloch. Đây là một bài đọc tuyệt vời và bao gồm loại điều này (cộng với cách triển khai chính xác bằng () khi bạn đang cố gắng thực hiện nhiều hơn so sánh so sánh dựa trên danh tính)


Với mục đích của mình, tôi đã cố gắng xem liệu một đối tượng có giá trị bằng nhau có trong ArrayList không. Tôi cho rằng nó là một loại hack. Cảm ơn bạn đã giới thiệu sách
Mantas Vidutis

3

Phím tắt từ JavaDoc :

boolean chứa (Object o)

Trả về true nếu danh sách này chứa phần tử đã chỉ định. Chính thức hơn, trả về true khi và chỉ khi danh sách này chứa ít nhất một phần tử e sao cho (o == null? E == null: o.equals (e))

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.