最近我读了这篇文章 开发人员工作文件。
该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。
我如何决定有效地实现这些方法?
最近我读了这篇文章 开发人员工作文件。
该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。
我如何决定有效地实现这些方法?
当前回答
为了帮助你检查重复的对象,我们需要一个自定义的等号和hashCode。
Since hashcode always returns a number its always fast to retrieve an object using a number rather than an alphabetic key. How will it do? Assume we created a new object by passing some value which is already available in some other object. Now the new object will return the same hash value as of another object because the value passed is same. Once the same hash value is returned, JVM will go to the same memory address every time and if in case there are more than one objects present for the same hash value it will use equals() method to identify the correct object.
其他回答
它在使用值对象时很有用。以下摘自Portland Pattern Repository:
Examples of value objects are things like numbers, dates, monies and strings. Usually, they are small objects which are used quite widely. Their identity is based on their state rather than on their object identity. This way, you can have multiple copies of the same conceptual value object. So I can have multiple copies of an object that represents the date 16 Jan 1998. Any of these copies will be equal to each other. For a small object such as this, it is often easier to create new ones and move them around rather than rely on a single object to represent the date. A value object should always override .equals() in Java (or = in Smalltalk). (Remember to override .hashCode() as well.)
简单地说,Object中的equals-方法检查引用是否相等,当属性相等时,类的两个实例在语义上仍然相等。例如,当将对象放入使用等号和hashcode(如HashMap和Set)的容器中时,这很重要。假设我们有这样一个类:
public class Foo {
String id;
String whatevs;
Foo(String id, String whatevs) {
this.id = id;
this.whatevs = whatevs;
}
}
我们创建了两个具有相同id的实例:
Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");
如果不重写等号,我们将得到:
A.equals (b)为假,因为它们是两个不同的实例 a.equals(a)为真,因为它是同一个实例 b.equals(b)为真,因为它是同一个实例
正确吗?也许吧,如果这是你想要的。但假设我们希望具有相同id的对象是相同的对象,不管它是否是两个不同的实例。我们重写等号(和hashcode):
public class Foo {
String id;
String whatevs;
Foo(String id, String whatevs) {
this.id = id;
this.whatevs = whatevs;
}
@Override
public boolean equals(Object other) {
if (other instanceof Foo) {
return ((Foo)other).id.equals(this.id);
}
}
@Override
public int hashCode() {
return this.id.hashCode();
}
}
至于实现equals和hashcode,我建议使用Guava的helper方法
这两个方法都在Object类中定义。两者都是最简单的实现。所以当你需要你想给这些方法添加更多的实现时你就可以在你的类中重写。
对于对象中的equals()方法只检查它在引用上的相等性。如果你也需要比较它的状态,那么你可以像在String类中那样重写它。
身份不是平等。
= operator ==测试标识符。 equals(Object obj)方法比较相等性测试(即我们需要通过重写方法来告诉相等)
为什么我需要重写Java中的equals和hashCode方法?
首先我们要理解等号法的用法。
为了识别两个对象之间的差异,我们需要重写equals方法。
例如:
Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.
------------------------------
Now I have overriden Customer class equals method as follows:
@Override
public boolean equals(Object obj) {
if (this == obj) // it checks references
return true;
if (obj == null) // checks null
return false;
if (getClass() != obj.getClass()) // both object are instances of same class or not
return false;
Customer other = (Customer) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference
return false;
return true;
}
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2); // returns true by our own logic
现在hashCode方法很容易理解了。
hashCode生成整数,以便将对象存储在HashMap、HashSet等数据结构中。
假设我们有如上所述的override equals Customer方法,
customer1.equals(customer2); // returns true by our own logic
在处理数据结构时,我们将对象存储在桶中(桶是文件夹的花哨名称)。如果我们使用内置哈希技术,对于以上两个客户,它会生成两个不同的哈希码。所以我们把相同的对象存储在两个不同的地方。为了避免这类问题,我们也应该基于以下原则重写hashCode方法。
不相等的实例可能具有相同的hashcode。 相同的实例应该返回相同的hashcode。
Joshua Bloch在Effective Java上说
必须在重写equals()的每个类中重写hashCode()。如果不这样做,将违反Object.hashCode()的一般约定,这将阻止您的类与所有基于哈希的集合(包括HashMap、HashSet和Hashtable)一起正常运行。
让我们通过一个例子来理解它,如果我们重写equals()而不重写hashCode()并尝试使用Map会发生什么。
假设我们有这样一个类,如果MyClass的两个对象的importantField相等(使用eclipse生成的hashCode()和equals()),则MyClass的两个对象相等
public class MyClass {
private final String importantField;
private final String anotherField;
public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}
想象一下你有这个
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
只覆盖等于
如果只有equals被覆盖,那么当你调用myMap.put(first,someValue)首先将哈希到某个桶,当你调用myMap.put(second,someOtherValue)它将哈希到其他一些桶(因为它们有不同的hashCode)。所以,尽管它们是相等的,因为它们不散列到同一个桶,映射无法意识到这一点,它们都留在映射中。
虽然如果重写hashCode()就没有必要重写equals(),但让我们看看在这种特殊情况下会发生什么:我们知道MyClass的两个对象是相等的,如果它们的importantField相等,但我们没有重写equals()。
只覆盖hashCode
如果你只覆盖hashCode,那么当你调用myMap.put(first,someValue)时,它首先接受,计算它的hashCode并将其存储在给定的bucket中。然后,当您调用myMap.put(second,someOtherValue)时,它应该根据Map文档将first替换为second,因为它们是相等的(根据业务需求)。
但问题是,equals没有被重新定义,所以当map哈希second并遍历bucket时,寻找是否有一个对象k,使得second.equals(k)为真,它不会找到任何对象,因为second.equals(first)将为假。
希望这是清楚的