八股文 | hashcode与equals
Java中的hashcode方法
哈希表(也叫散列表)是一种常用的数据结构,通常被用来提高查找效率。无论哈希表总中有多少条数据,插入和查找的时间复杂度都是为O(1)。
//object 方法
public native int hashCode();
根据这个方法的声明可知,该方法返回一个int类型的数值,并且是(native)本地方法,因此在Object类中并没有给出具体的实现。
实际上哈希函数就是从键值到索引的过程,需要通过哈希函数来进行计算。
一. hashCode方法的作用
hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。
Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。
//java.util.HashMap的中put方法
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
//当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值
int hash = hash(key.hashCode());
//用一个table保存已经存进去的对象的hashcode值
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果存在,则更新value值
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
//将新的元素添加到HashMap中
addEntry(hash, key, value, i);
return null;
}
hashCode方法的存在是为了减少equals方法的调用次数,从而提高程序效率。
二. equals方法和hashCode方法
对于两个对象,如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等;
如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同;
如果两个对象的hashcode值不等,则equals方法得到的结果必定为false;
如果两个对象的hashcode值相等,则equals方法得到的结果未知。
import java.util.HashMap;
class People{
private String name;
private int age;
public People(String name,int age) {
this.name = name;
this.age = age;
}
public void setAge(int age){
this.age = age;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return this.name.equals(((People)obj).name) && this.age== ((People)obj).age;
}
// @Override
// public int hashCode() {
// // TODO Auto-generated method stub
// return name.hashCode()*37+age;
// }
}
public class Main {
public static void main(String[] args) {
People p1 = new People("Jack", 12);
System.out.println(p1.hashCode());
HashMap<People, Integer> hashMap = new HashMap<People, Integer>();
hashMap.put(p1, 1);
System.out.println(hashMap.get(new People("Jack", 12)));
}
}
输出结果:
原因:
默认情况下,hashCode方法是将对象的存储地址进行映射。
p1指向的对象和 System.out.println(hashMap.get(new People("Jack", 12)));
这句中的new People("Jack", 12)
生成的是两个对象,它们的存储地址肯定不同。
//HashMap的get方法的具体实现:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
所以,在重写equals方法的同时,必须重写hashCode方法。
- 在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。
- 如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
- 如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。
- 设计hashCode方法和equals方法的时候,如果对象中的数据易变,则最好在equals方法和hashCode方法中不要依赖于该字段。