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

Скажем, я создаю один объект и добавляю его в свой 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?

10 ответов

Решение

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

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

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

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

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

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

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

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

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

Просто хотел отметить, что следующая реализация неверна, когда 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;
    }
}

На других постерах был рассмотрен вопрос о том, как работает contains().

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

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

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

Ярлык из JavaDoc:

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

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

record отменяет

Ты сказал:

другой объект с точно таким же вводом конструктора

… а также …

Предположим, что конструктор не делает ничего смешного с вводом, а переменные, хранящиеся в обоих объектах, идентичны.

Как объясняется в других ответах, вы должны переопределить Object#equals метод для работать.

В Java 16+, то запись функция автоматически переопределяет этот метод для вас.

Запись - это краткий способ написать класс, основная цель которого - прозрачная и неизменная передача данных. По умолчанию вы просто объявляете поля членов. Компилятор неявно создает конструктор, геттеры, & и.

Логика equalsпо умолчанию это сравнение каждого поля-члена одного объекта с аналогом в другом объекте того же класса. Аналогично, реализации по умолчанию hashCode а также toString методы также рассматривают каждое поле-член.

      record Thing( int amount ) {} ;

Вот и все, это весь код, который вам нужен для полнофункционального класса только для чтения, без обычного шаблонного кода .

Пример использования.

      Thing x = new Thing( 100 ) ; 
Thing y = new Thing( 100 ) ; 
boolean parity = x.equals( y ) ;

При запуске.

четность = истина

Вернуться к твоему List#contains вопрос.

      Thing x = new Thing( 100 );
List < Thing > things =
        List.of(
                new Thing( 100 ) ,
                new Thing( 200 ) ,
                new Thing( 300 )
        );

boolean foundX = things.contains( x );

При запуске.

foundX = истина


Бонус: запись может быть объявлена ​​локально в методе. Или, как обычный класс, вы можете объявить запись как вложенный класс или как отдельный класс.

Другие вопросы по тегам