假设我创建了一个对象并将其添加到我的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
吗?
答案 0 :(得分:316)
ArrayList implements
列表接口。
如果您使用contains
方法查看Javadoc for List
,您会看到它使用equals()
方法来评估两个对象是否相同。
答案 1 :(得分:51)
我认为正确的实施应该是
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;
}
}
答案 2 :(得分:14)
ArrayList使用类中实现的equals方法(您的案例Thing类)来进行等于比较。
答案 3 :(得分:10)
通常,每次覆盖hashCode()
时,您也应该覆盖equals()
,即使只是为了提升效果。 HashCode()
决定在进行比较时您的对象被排序到哪个“桶”,因此equal()
计算为true的任何两个对象都应返回相同的hashCode
value()
。我不记得hashCode()
的默认行为(如果它返回0,那么你的代码应该工作但速度很慢,但是如果它返回地址那么你的代码就会失败)。当我的代码失败时,我确实记得很多次,因为我忘了覆盖hashCode()
。 :)
答案 4 :(得分:7)
它在对象上使用equals方法。因此,除非Thing覆盖equals并使用存储在对象中的变量进行比较,否则它将不会在contains()
方法上返回true。
答案 5 :(得分: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;
}
}
答案 6 :(得分:4)
其他海报已经解决了关于contains()如何运作的问题。
您的问题同样重要的一个方面是如何正确实现equals()。对此的答案实际上取决于这个特定类的对象相等的构成。在您提供的示例中,如果您有两个不同的对象,它们都有x = 5,它们是否相等?这实际上取决于你想要做什么。
如果您只对对象相等感兴趣,则.equals()的默认实现(Object提供的实现)仅使用标识(即this == other)。如果这是你想要的,那么就不要在你的类上实现equals()(让它继承自Object)。你编写的代码虽然是正确的,但如果你想要识别,那么它将永远不会出现在真正的类b / c中,与使用默认的Object.equals()实现相比,它没有任何好处。
如果您刚开始使用这些内容,我强烈推荐Joshua Bloch撰写的Effective Java一书。这是一个很好的阅读,并涵盖了这种事情(以及当你尝试做的不仅仅是基于身份的比较时如何正确地实现equals())
答案 7 :(得分:2)
来自JavaDoc的快捷方式:
布尔 包含(对象o)
如果此列表包含指定的元素,则返回true。更正式的, 当且仅当此列表包含至少一个元素e时才返回true (o == null?e == null:o.equals(e))
答案 8 :(得分:0)
record
覆盖 equals
你说:
<块引用>另一个具有完全相同构造函数输入的对象
……和……
<块引用>假设构造函数不对输入做任何有趣的事情,并且存储在两个对象中的变量是相同的。
正如其他答案所解释的,您必须覆盖 Object#equals
方法才能使 List#contains
工作。
在 Java 16+ 中,record 功能会自动为您覆盖该方法。
记录是编写类的一种简短方式,其主要目的是透明和不可变地传达数据。默认情况下,您只需声明成员字段。编译器隐式创建构造函数、getter、equals
& hashCode
和 toString
。
默认情况下,equals
的逻辑是将一个对象的每个成员字段与同一类的另一个对象中的对应成员字段进行比较。同样,hashCode
和 toString
方法的默认实现也会考虑每个成员字段。
record Thing( int amount ) {} ;
就是这样,这是一个功能齐全的只读类所需的全部代码,没有通常的 boilerplate code。
示例用法。
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 = 真
额外功能:可以在方法中本地声明记录。或者像传统类一样,您可以将记录声明为嵌套类或单独的类。