java的equals方法问题。
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java的equals方法问题。相关的知识,希望对你有一定的参考价值。
mport java.util.*;
/**
*
* @author asrock
*/
public class TestColl
public static void main(String[] args)
Collection c = new ArrayList();
c.add("hello");
c.add(new Name("f1","f2"));
c.add(new Integer(100));
c.remove("hello");
c.remove(new Integer(100));
System.out.println(c.remove(new Name("f1","f2"))); // 这里并没有显式的调用equals()方法。怎么知道new Name("f1","f2")与上面的new Name("f1","f2")相等。
System.out.println(c);
class Name
public Name(String firstName,String lastName)
this.firstName = firstName;
this.lastName = lastName;
private String firstName,lastName;
public String getFirstName()
return firstName;
public String getLastName()
return lastName;
public String toString()
return firstName + " " + lastName;
//这里重写了equals方法
public boolean equals(Object obj)
if (obj instanceof Name)
Name name = (Name) obj;
return (firstName.equals(name.firstName))&&(lastName.equals(name.lastName));
return super.equals(obj);
boolean remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。更确切地讲,如果此 collection 包含一个或多个满足
(o==null ? e==null : o.equals(e)) 的元素 e,则移除这样的元素。如果此
collection 包含指定的元素(或者此 collection 由于调用而发生更改),则返回 true 。
Name的equals方法是在remove方法中调用的,要想判断两个Name对象是否相等,可以用两个变量保存起来然后再比较就成了。追问
有点看不懂api文档的这个解释,不知道那里指出了调用equals方法
(o==null ? e==null : o.equals(e)) 是这一句吗?但是O是对象引用 而e则是元素 似乎无法比较呀?
还有就是虽然在Name类中重写了equals方法 但是并不能保证就会调用Name类中重写的equals方法呀
求解释 不胜感激
c.remove(new Name("f1","f2")),c是ArrayList的实例,下面是具体实现:
public boolean remove(Object o)
if (o == null)
for (int index = 0; index < size; index++)
if (elementData[index] == null)
fastRemove(index);
return true;
else
for (int index = 0; index < size; index++)
if (o.equals(elementData[index]))
fastRemove(index);
return true;
return false;
其中参数Object o就是new Name("f1","f2"),对Name的equals方法的调用就在这里:
if (o.equals(elementData[index]))
首先你得明白一个问题:new出来的他会开辟一个新地址,不会和前面一样比如:String temp = new String("abc"); String temp2 = new String("abc");如果你输出temp temp2的地址是不一样的。虽然内容一样,不要主观臆断。必须用地址所指向的值来进行判断才能得出值是否相等,你这里也没有指针指向你new 的对象,用一次后就找不到了,只会变成垃圾被处理,可以在new 的时候进行对比 new Name("f1","f2").equals(...)进行判断,如果没有保存指向,其它地方就不能用了。 参考技术B System.out.println(c.contains(new Name("f1","f2")));
System.out.println(c.remove(new Name("f1","f2")));
System.out.println(c.contains(new Name("f1","f2"))); 参考技术C ArrayList的remove方法中调用了equals方法来判断。 参考技术D 你调用了remove(),remove()又在方法体里面调用了equals(),你可以在equals()方法里面写上System.out.println("......")就可以知道它隐式调用equals()了追问
嗯 这个试了下 果然知道是调用了equals方法 但为什么能调用这个重写的方法。而不是原先的remove中的equals方法 。还是有点不太明白。
覆盖 Java 中的 hashCode() 和 equals() 方法
【中文标题】覆盖 Java 中的 hashCode() 和 equals() 方法【英文标题】:Overwriting the hashCode() and equals() method in Java 【发布时间】:2020-06-09 12:30:51 【问题描述】:正如标题所示,我的问题与重写 hashCode() 和 equals() 方法有关。但是,这也不完全正确,我只是不知道用另一种方式来总结我的问题。
我有一个对象Label
,它包含多个组件,其中一个是List
,它包含多个Node
类型的对象。 Label
的一个示例是:[(n1, n2, n3), (n4, n5)]
。我想存储在LinkedHashSet
中生成的所有唯一Label
对象。但是,这并没有按预期工作。假设LinkedHashSet
当前包含上述Label
,并且我们现在生成了一个名为other
的新Label
,结果证明它包含与已添加标签相同的节点,因此也包含[(n1, n2, n3), (n4, n5)]
。由于它具有相同的节点列表,Label
中的其他组件也相同。我不会在这里解释为什么,只是假设它是,因为事实就是如此。但是,当检查LinkedHashSet
是否已经包含Label
时,它会返回false
,因为对象具有不同的对象ID。
一种方法是在 LinkedHashSet
上编写一个 for 循环,并将新标签与 LinkedHashSet
中的所有标签进行比较,但这在运行时间方面会非常昂贵,所以我正在寻找一个更便宜的选择。欢迎提出任何建议!
另一种方法是调整 equals()
方法,但这行不通,因为我还必须调整 hashCode()
方法,但我不知道如何更改它才能使其正常工作。
【问题讨论】:
hashCode
或 equals
是否已在 Label
上被覆盖?除非这些被覆盖,否则将使用hashCode
的默认实现,它对应于对象标识。
看看这篇文章:mkyong.com/java/java-how-to-overrides-equals-and-hashcode
不清楚您的问题与标准“如何在 Java 中覆盖 equals 和 hashCode?”有什么区别
缓存Label
对象的哈希码会提供很好的效率。
@kaya3 OP 不确定覆盖 equals
和 hashCode
实际上是他(或她)问题的解决方案。
【参考方案1】:
在这里实现equals(...) 和hashCode(...) 是正确的方法。通常人们使用他们的 IDE 为他们生成这些方法,这是 Java 7+ 的样子:
@Override
public boolean equals(Object o)
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Label label = (Label) o;
return Objects.equal(nodes, label.nodes);
@Override
public int hashCode()
return Objects.hashCode(nodes);
如果您有多个字段,则变为:
@Override
public boolean equals(Object o)
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Label label = (Label) o;
return Objects.equal(nodes, label.nodes) &&
Objects.equal(other, label.other);
@Override
public int hashCode()
return Objects.hashCode(nodes, other);
但是,这只有在 Node
的实现也实现 equals(...)
和 hashCode(...)
时才有效。
【讨论】:
【参考方案2】:您绝对不想“在LinkedHashSet
上循环并比较...” 检测重复项是LinkedHashSet
应该做的事情。为此,它需要equals
和hashCode
的适当实现。
您可能明白您需要实现equals
,以便当Label
“等于”另一个Label
时返回true,但是您可以定义它。如果两个标签在它们具有相同节点时相等,那么是的,您几乎必须查看所有节点并检查它们是否相同。
剩下hashCode
。您必须实现hashCode
与equals
一致,即如果两个标签相等,则它们必须具有相同的哈希码。那是因为LinkedHashSet
将使用哈希码来确定Label
所在的桶,然后使用equals
将新的Label
与桶中已经存在的Label
进行比较。如果两个标签相等但生成不同的哈希码,LinkedHashSet
将无法将它们检测为重复。
最简单的做法是将所有节点的哈希码合并到Label
的哈希码中。比如:
int hashCode()
int hc = 1;
for (Node n : allMyNodes)
hc = hc * 31 + n.hashCode();
return hc;
如果有很多节点,并且除非它们“相等”,否则两个标签不太可能共享同一个节点,您可以只使用第一个节点的哈希码,而不是翻遍所有节点。
【讨论】:
由于Java 7+一般不需要自己处理复杂的hashCode实现,可以通过字段列表调用Objects.hashCode(...)。 另外在这里你不需要自己创建列表的 hashCode 作为列表实现可能作为 hashCode 实现。所以你可以做return Objects.hashCode(allMyNodes)
,仅此而已。
你在这两种情况下都是正确的,事实上List
接口将hashCode
定义为我编写的函数。 (虽然我犯了一个错误,它应该初始化为 1,而不是 0。)但我认为展示实际发生的事情很有用。
修复了这个功能。
@WillisBlackburn 感谢您的清晰解释!我喜欢你对 hashCode 方法的建议,但恐怕这对我的方法来说还不够。如,我必须非常小心,因为我可以有多个标签具有相同的节点,但在列表的不同顺序/部分,因此需要不同的 hashCodes。如果equals()方法返回true,hashCode()返回false,那么新标签是否会被添加到linkedHashSet中?那会是我的偏好。【参考方案3】:
在您的Node
(或Label
)课程中使用lombok
中的@EqualsAndHashCode
。
然后您可以将节点添加到您的LinkedHashSet
。
【讨论】:
如果你一开始还不知道 equals 和 hashCode 是如何工作的,最好不要开始使用 Lombok,它会让你几乎无法理解和调试它。以上是关于java的equals方法问题。的主要内容,如果未能解决你的问题,请参考以下文章
java 集合中重写hashCode方法和重写equals方法啥关系?