为什要重写hashcode?
hashcode方法得到一个hash值其实是要起到一个比较作用,比较两个未知的东西是不是同一个东西,因为我们要求hashcode方法产生的hash值对于”同一个东西“得到的hash值是一样的。
那这种特性可以做到去重的效果,因为同一个东西的hash值是一样的,当我们将内容添加进容器时,如果通过hash值来得到存放的位置,那么相同的东西,一定会被分配到同一个位置,假设不同东西的hash值不同,那么我们只需要判断通过hash得到的位置上是否已经存放内容,如果没有存放说明容器没有我当前要存放的东西,那我就放入其中,如果有说明容器中已经有一样的东西存入,那就不必再存入了。
hash值固然强大,但是任然会出现不同的东西,计算出的hash值一样的情况,我们称之为哈希冲突。哈希冲突无法完全解决只能避免。
hash值很强大,hashcode方法一个重要的功能就是我传入同一个东西时,返回给我的hash值一定是一样的,如果不重写hashcode方法,默认使用object的hashcode方法,这一原始计算hash值的方法是通过不同对象存放的内存地址计算hash值的,那么两个内容上一致逻辑上判定成一样的东西,但由于存放地址不同会产生不同的hash值,于是原始的hashcode方法无法满足我们的需求,我们必须要重写,以满足我们的需求。
为什要重写equals?
哈希冲突由谁来解决呢?最简单直接的办法就是当哈希冲突时我们直接比较冲突对象的内容来判定它们是否是同一个东西。那为什么不直接来判定内容呢?从根源上消除哈希冲突,不使用hashcode方法?因为直接比较每个对象中的内容远比通过内容产生一个哈希值先筛选如果哈希冲突再判断内容来的高效的多。
那为什么要重写hashcode的同时重写equals呢?因为原始的object提供的equals方法只是通过==来判断两个东西是否相等,对于基本数据类型足够,但是引用数据类型比较地址值的话原始equals方法完全无法满足我们通过内容判定两个对象是否相同的需求。
hashset存放数据有着无序、不重复的特点,底层就会用到hash值去重的策略,下面以hashset为例,进一步探究上面已经解决的问题。
//hashset的添加元素的操作,底层使用的是map的put方法,传入两个值一个是我们要添加的元素,一个是空的object对象
//k-我们需要存的元素,v-一个空的object对象
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//put方法又使用的hashmap中的putVal方法
/*
参数1:key(我们需要添加的元素)的哈希值
参数2:key(我们需要添加的元素)
参数3:value(创建的空object对象)
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}