</pre> equals 和hashCode方法都是Object类中的方法。<p></p><pre name="code" class="java"> public boolean equals(Object obj) {
return (this == obj);
}
public native int hashCode();
equals方法在比较时,此处使用的是==,比较的引用(内存地址),当引用相同时则返回true
hashCode使用的是hash函数产生的hash码。hash函数有一定的特性,相同的对象或字符hash码一定相同,具有相同hash码的对象或字符并不一定相同。
由于Ojbect是所有类的基类,当自定义的类需要重写equals方法时,一定要重写hashCode方法。可以这么理解,hashCode将对象映射成为一串地址,该对象就存在地址所对应的桶里面(一个桶里面存hashCode相同的对象),当要使用equals判断两个对象是否相同时,先要通过hashCode找到相应的桶,然后再桶里通过遍历找到相等的两个对象。
hash在hashMap ,hashTable 等实现Map接口的集合类使用的比较多。hashMap中实现equals和hashCode的源码如下:
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
可以看出hashMap 中重写了equals方法和hashCode方法。由于hashMap不允许有重复的key出现,所以在put方法中寻找时做了特殊处理,先根据key的hash码定位到Entry数组的具体位置,然后依次遍历该位置的链表,找到相同的key则覆盖掉其value。源代码如下:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
我们知道,我们使用的大多数情况下key的值为String类型,如果key的值为Object会出现什么情况?
key为String的情况:
<pre name="code" class="java">mport java.util.HashMap;
import java.util.Map;
public class Equals {
public static void main(String[] args) {
Map<Object,Object> map = new HashMap<Object,Object>();
String s1 = "abc";
map.put(s1, new Employee("1", "tom"));
String s2 = new String("abc");
System.out.println(map.get(s1));
System.out.println(map.get(s1)==map.get(s2));
Employee e1 = new Employee("1", "tom");
map.put(e1, s1);
Employee e2 = new Employee("1", "tom");
System.out.println(map.get(e1));
System.out.println(map.get(e2));
}
}
class Employee{
private String id;
private String name;
public Employee(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
输出结果
<pre name="code" class="java">com.test.Employee@4f1d0d
true
abc
null
同样是不同的对象s1 、s2 和e1、e2,为何对象s2可以取出s1作为Key的value,而e2却不能取出以e1作为key的value呢。通过查看Strsing的源码,我们发现该类重写了equals和hashCode方法,具体源码如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
按照此原理,我们可以在自定义的类里重写equals和hashCode方法。
class Employee{
private String id;
private String name;
public Employee(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if(this==obj) return true;
if( obj instanceof Employee){
Employee e = (Employee) obj;
if(e.getId()==this.id) return true;
}
return false;
}
@Override
public int hashCode() {
return id.hashCode();
}
}
运行结果:
com.test.Employee@31
true
abc
abc
由此可见,Object做为Map中的Key时一定要重写equals和hashCode方法