先看一个例子
首先定义一个user类。
package com.text.tool; public class User { int id; User(int id) { this.id = id; } public String toString() { return "key="+id; } }
其次写一个测试类,创建一个HashMap,在map中User对象作为KEY。
package com.text.tool; import java.util.HashMap; import java.util.Map; public class TestMap { public static void main(String[] args) { Map map = new HashMap(); map.put(new User(1), 1); map.put(new User(2), 1); map.put(new User(3), 1); System.out.println(map.keySet()); System.out.println(map.get(new User(1)) } }
输出结果,让人疑惑的是为什么MAP数组中匹配不到new User(1)。
[key=1, key=2, key=3] null
我们可以简单看下HashMap源码,在源码中获取到key对应value做了一个判断,e.hash == hash && eq(k, e.key)也就是对key的hashcode进行比对,以及进行equals判断。
我们传入的对象User默认继承Object,所以equals及hashCode默认是Object的方法,而此时这里的equals比较的是两个对象的内存中的地址并非value,所以第二个new User(1)就无法再MAP数组中取到value了。
public Object get(Object key) { Object k = maskNull(key); int hash = hash(k); int i = indexFor(hash, table.length); Entry e = table[i]; while (true) { if (e == null) return e; if (e.hash == hash && eq(k, e.key)) return e.value; e = e.next; } }
public boolean equals(Object obj) { return (this == obj); }
public native int hashCode();
在User类中重写equals及hashcode方法,在启用main方法就不会有问题了。
package com.text.tool; public class User { int id; User(int id) { this.id = id; } public String toString() { return "key="+id; } public int hashCode() { return id; } public boolean equals(Object o) { return o instanceof User && (id == ((User) o).id); } }
执行MAIN方法输出结果:
[key=2, key=1, key=3] 1
截取think in java一段描述:
equals()相等的两个对象,hashcode()一定相等;
equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。
反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解释下第3点的使用范围,我的理解是在object、String等类中都能使用。在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了;在String类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时,
Hashcode()方法根据String类的重写代码的分析,也可知道hashcode()返回结果也会相等。以此类推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。
附下哈希表结构:
在一个数组中存储对象时,通过 hashCode 得到的哈希值来计算数组的索引位置(通常是求余运算),然后根据这个索引位置进行存取。多个对象计算出来的索引位置相同(叫hash冲突)时,用链表保存。冲突怎么保证取到的就是自己呢?那么就要用到 Object.equals() 方法。
所以要把对象存储在像 hash table类似的数据结构(比如:HashSet)中,hashCode 与 equals 要成对实现。
部分内容来源: