最近我阅读了这篇Developer Works文档。
该文档主要介绍了如何有效和正确地定义hashCode()
和equals()
方法,但是我不明白为什么需要覆盖这两个方法。
我该如何决定高效地实现这些方法?
最近我阅读了这篇Developer Works文档。
该文档主要介绍了如何有效和正确地定义hashCode()
和equals()
方法,但是我不明白为什么需要覆盖这两个方法。
我该如何决定高效地实现这些方法?
如果您不覆盖它们,那么您将使用Object中的默认实现。
鉴于实例相等性和哈希码值通常需要了解对象的组成部分,因此通常需要在您的类中重新定义它们以具有任何实际含义。
1) 常见误区如下面的示例所示。
public class Car {
private String color;
public Car(String color) {
this.color = color;
}
public boolean equals(Object obj) {
if(obj==null) return false;
if (!(obj instanceof Car))
return false;
if (obj == this)
return true;
return this.color.equals(((Car) obj).color);
}
public static void main(String[] args) {
Car a1 = new Car("green");
Car a2 = new Car("red");
//hashMap stores Car type and its quantity
HashMap<Car, Integer> m = new HashMap<Car, Integer>();
m.put(a1, 10);
m.put(a2, 20);
System.out.println(m.get(new Car("green")));
}
}
绿色汽车未找到
2. hashCode()引起的问题
问题是由未重写hashCode()
方法引起的。 equals()
和hashCode()
之间的约定是:
If two objects have the same hash code, they may or may not be equal.
public int hashCode(){
return this.color.hashCode();
}
在使用值对象时很有用。以下是来自Portland Pattern Repository的摘录:
值对象的例子包括数字、日期、货币和字符串等。通常,它们是小型对象,被广泛地使用。它们的身份基于它们的状态而不是它们的对象身份。这样,您可以拥有多个相同概念的值对象的副本。
因此,我可以拥有代表日期1998年1月16日的对象的多个副本。其中任何一个副本都将相互相等。对于这样一个小对象,通常更容易创建新的对象并移动它们,而不是依赖单个对象来表示日期。
值对象应该始终在Java中重写.equals()(或在Smalltalk中为=)。 (记得同时重写.hashCode()。)
class A {
int i;
// Hashing Algorithm
if even number return 0 else return 1
// Equals Algorithm,
if i = this.i return true else false
}
hashCode()
计算哈希值以确定桶,并使用 equals()
方法查找值是否已存在于桶中。如果不存在,则添加该值,否则将替换为当前值。hashCode()
查找 Entry(桶)首先,再使用 equals()
查找 Entry 中的值。如果两者都被重写,
Map<A>
Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...
如果equals没有被覆盖
Map<A>
Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..
Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...
哈希码相等契约
myMap.put(first,someValue)
时,它会获取first,计算其hashCode并将其存储在给定的bucket中。 然后,当您调用myMap.put(first,someOtherValue)
时,根据Map文档,它应该替换第一个对象为第二个对象,因为它们相等(根据我们的定义)。 ”myMap
添加内容时,应该是“second”对象,如myMap.put(second,someOtherValue)
。equals()
但没有覆盖hashcode()
,除非你或其他人将该类类型用于像HashSet
这样的哈希集合中,否则你不会遇到任何问题。
之前已经有人清楚地多次解释了文档理论,我只是在这里提供一个非常简单的例子。equals()
含义的类: public class Rishav {
private String rshv;
public Rishav(String rshv) {
this.rshv = rshv;
}
/**
* @return the rshv
*/
public String getRshv() {
return rshv;
}
/**
* @param rshv the rshv to set
*/
public void setRshv(String rshv) {
this.rshv = rshv;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Rishav) {
obj = (Rishav) obj;
if (this.rshv.equals(((Rishav) obj).getRshv())) {
return true;
} else {
return false;
}
} else {
return false;
}
}
@Override
public int hashCode() {
return rshv.hashCode();
}
}
import java.util.HashSet;
import java.util.Set;
public class TestRishav {
public static void main(String[] args) {
Rishav rA = new Rishav("rishav");
Rishav rB = new Rishav("rishav");
System.out.println(rA.equals(rB));
System.out.println("-----------------------------------");
Set<Rishav> hashed = new HashSet<>();
hashed.add(rA);
System.out.println(hashed.contains(rB));
System.out.println("-----------------------------------");
hashed.add(rB);
System.out.println(hashed.size());
}
}
true
-----------------------------------
true
-----------------------------------
1
我对结果感到满意。但是如果我没有覆盖hashCode()
,那么会导致噩梦,因为具有相同成员内容的Rishav
对象将不再被视为唯一的,因为hashCode
将是不同的,这是由默认行为生成的,以下是可能的输出:
true
-----------------------------------
false
-----------------------------------
2
Java中的Equals和Hashcode方法
它们是java.lang.Object类的方法,该类是所有类(包括自定义类和在java API中定义的其他类)的超类。
实现:
public boolean equals(Object obj)
public int hashCode()
public boolean equals(Object obj)
此方法只是检查两个对象引用x和y是否引用同一个对象。即它检查x == y。
它是自反的:对于任何引用值x,x.equals(x)应返回true。
它是对称的:对于任何引用值x和y,如果且仅当y.equals(x)返回true时,x.equals(y)应返回true。
它是传递的:对于任何引用值x、y和z,如果x.equals(y)返回true并且y.equals(z)返回true,则x.equals(z)应返回true。
它是一致的:对于任何引用值x和y,多次调用x.equals(y)始终一致地返回true或始终一致地返回false,前提是在对象上使用的equals比较中未修改任何信息。
对于任何非空引用值x,x.equals(null)应返回false。
public int hashCode()
此方法返回调用此方法的对象的哈希码值。此方法将哈希码值作为整数返回,并支持基于哈希的集合类(如Hashtable、HashMap、HashSet等)的好处。必须在每个覆盖equals方法的类中重写此方法。
hashCode的一般契约是:
在Java应用程序的执行期间,每当在同一对象上多次调用它时,hashCode方法必须一致地返回相同的整数,前提是在对象上使用的equals比较中未修改任何信息。
这个整数不需要从一个应用程序的执行到另一个应用程序的执行保持一致。
如果两个对象根据equals(Object)方法是相等的,则在每个对象上调用hashCode方法必须产生相同的整数结果。
并不要求如果两个对象根据equals(java.lang.Object)方法是不相等的,则在每个对象上调用hashCode方法必须产生不同的整数结果。然而,程序员应该知道,为不相等的对象产生不同的整数结果可能会改善哈希表的性能。
只要它们相等,相等的对象必须产生相同的哈希码,但是不相等的对象不需要产生不同的哈希码。
资源:
以上内容包含了关于IT技术的链接和图片。