Как метод ArrayList в объекте () оценивает объекты?

303

Скажем, я создаю один объект и добавляю его в свой ArrayList. Если я затем создам другой объект с точно таким же входом конструктора, будет ли contains()метод оценивать два объекта как одинаковые? Предположим, что конструктор не делает ничего смешного с вводом, и переменные, хранящиеся в обоих объектах, идентичны.

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;
    }
}

Это как это classдолжно быть реализовано, чтобы иметь contains()возвращение true?

Мантас Видутис
источник

Ответы:

339

ArrayList implementsинтерфейс списка.

Если вы посмотрите на Javadoc дляList в containsметоде вы увидите , что он использует equals()метод оценки , если два объекта являются одинаковыми.

Бинарный ботаник
источник
61
На случай, если вы планируете переопределить equals (), убедитесь, что вы также переопределяете метод hashcode (). Если вы не хотите, вещи могут работать не так, как ожидалось при использовании коллекций?
Мохд Фарид
34
Это правильный ответ, но учтите, что вам нужно изменить метод equals, чтобы он принимал Objectвместо a Thing. Если вы этого не сделаете, ваш метод равно не будет использоваться. :)
mdierker
1
Только что обнаружил, что в меню «Источник» у eclipse есть «Generate hashCode () and equals».
Владимир Крупач
Это отвечает на вопрос в заголовке, но не на вопрос в описании, т. Е. «Если я затем создам другой объект с точно таким же входом конструктора, будет ли метод contains () оценивать два объекта как одинаковые?»
разбой
3
Collectionsделать их вещи оптимизированным способом, то есть contains()сначала проверяет hashCodes двух объектов, а затем вызывает equals(). Если hashCodes разные (что всегда имеет место для двух разных экземпляров Thing), equals()метод вызываться не будет. Как правило, когда вы переопределяете equals(), вы не должны забывать hashCode()также переопределять .
Севастян Саванюк
52

Я думаю, что правильные реализации должны быть

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;
    }
}
ChristopheCVB
источник
1
ifутверждение не нужно. instanceofдостаточно.
Пол
@ Пол, о какой части заявления ты говоришь?
ChristopheCVB
4
object != nullУсловие не является необходимым, так как object instanceof Thingпроверка на предмет того , не нулевое значение тоже.
Александр Фарбер
15

ArrayList использует метод equals, реализованный в классе (класс Thing вашего случая), для сравнения.

Бхушан Бхангале
источник
12

Как правило, вы также должны переопределять hashCode()каждый раз, когда вы переопределяете equals(), даже если только для повышения производительности. HashCode()решает, в какое «ведро» будет отсортирован ваш объект при сравнении, поэтому любые два объекта, которые имеют equal()значение true, должны возвращать одно и то же hashCode value(). Я не могу вспомнить поведение по умолчанию hashCode()(если он возвращает 0, тогда ваш код должен работать, но медленно, но если он возвращает адрес, то ваш код потерпит неудачу). Я помню много раз, когда мой код не удался, потому что я забыл переопределить hashCode(). :)

alexloh
источник
7

Он использует метод equals для объектов. Таким образом, если переопределение Thing не равно и не использует переменные, хранящиеся в объектах для сравнения, оно не вернет true для contains()метода.

Ишай
источник
6
class Thing {  
    public int value;  

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

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

Вы должны написать:

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;
    }
}

Теперь это работает;)

Davide
источник
6
вы не должны делать Thing x = (Thing) o; без предварительной проверки, является ли другой объект нулевым
steelshark
5

Просто хотел заметить, что следующая реализация неверна, когда valueне является примитивным типом:

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;
    }
}

В этом случае я предлагаю следующее:

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;
    }
}
Джанер
источник
Как реализовать это, устраняя дубликаты?
Суджай
4

Другие авторы обращались к вопросу о том, как работаетве ().

Не менее важным аспектом вашего вопроса является правильная реализация equals (). И ответ на это действительно зависит от того, что составляет объектное равенство для этого конкретного класса. В приведенном вами примере, если у вас есть два разных объекта, каждый из которых имеет х = 5, они равны? Это действительно зависит от того, что вы пытаетесь сделать.

Если вас интересует только равенство объектов, тогда стандартная реализация .equals () (предоставленная Object) использует только идентичность (то есть this == other). Если это то, что вы хотите, то просто не реализуйте equals () в вашем классе (пусть он наследуется от Object). Код, который вы написали, хотя и является правильным, если вы идете за идентичностью, никогда не появится в реальном классе, потому что он не дает никакой выгоды по сравнению с использованием стандартной реализации Object.equals ().

Если вы только начинаете заниматься этим, я настоятельно рекомендую книгу «Эффективная Java» Джошуа Блоха. Это отличное чтение, и оно охватывает такие вещи (плюс, как правильно реализовать equals (), когда вы пытаетесь сделать больше, чем сравнение на основе идентичности)

Кевин Дэй
источник
В своих целях я пытался увидеть, есть ли объект равной ценности в ArrayList. Я полагаю, это своего рода взлом. Спасибо за рекомендацию книги
Мантас Vidutis
3

Ярлык из JavaDoc :

логическое значение содержит (объект o)

Возвращает true, если этот список содержит указанный элемент. Более формально, возвращает true тогда и только тогда, когда этот список содержит хотя бы один элемент e такой, что (o == null? E == null: o.equals (e))

Колодин
источник