Как метод 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()
оценивается как истина должна вернуть то же самое hashCode
value()
, Я не могу вспомнить поведение по умолчанию 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 = истина
Бонус: запись может быть объявлена локально в методе. Или, как обычный класс, вы можете объявить запись как вложенный класс или как отдельный класс.