hashMap
HashMap的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。HashMap中主要是通过key的hashCode来计算hash值的,只要hashCode相同计算出来的hash值就一样。如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就出现了所谓的hash冲突
数组的每个元素都是一个单链表的头节点,链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中
当向HashMap中添加元素时,我们先通过key的hash值经过一系列的计算算出所在数组中的位置,如果此位置上已经有元素了,那么我们在通过equals方法与此位置上的单向链表内的元素一一比较, 如果有相同的key,就会替换原有的key-value,如果没有,添加在此单向链表中. 如果此位置上没有元素,就直接存储在此位置上对应单向链表的头节点上。
现在的问题就是怎么解决作为key对象的hash值有可能与其他key的hash值冲突问题(此时应该尽量避免单向链表的产生)
怎么解决:
Object类的hashCode方法是:返回对象的散列值,这个值是根据对象的内存地址经过hash算法处理后的一个int值。(如果不重写,这个算法不可控,相撞的几率就非常大)因此,我们需要重写,让相撞的几率更小。
一、 关于equals
object类中默认的实现方式是:return this == obj,就是说,只有this 和 obj引用同一个对象,才会返回true。而我们往往需要用
equals来判断 2个对象是否等价,而非验证他们的唯一性。这样我们在实现自己的类时,就要重写equals.
按照约定,equals要满足以下规则。
自反性: x.equals(x) 一定是true
对null: x.equals(null) 一定是false
对称性: x.equals(y) 和 y.equals(x)结果一致
传递性: a 和 b equals , b 和 c equals,那么 a 和 c也一定equals。
一致性:x.equals(y)的第一次调用为true,那么x.equals(y)的第二次、第三次、第n次调用也均为true,前提条件是没有修
改x也没有修改y
关于hashCode方法,一致的约定是:
若重写了equals(Object obj)方法,则有必要 重写hashCode()方法。
若两个对象equals(Object obj)返回true, 则hashCode()有必要也返回相同的int数。
若两个对象equals(Object obj)返回false,则hashCode()不一定返回不同的int数。
(但为不相等的对象生成不同hashCode值可以提高哈希表的性能)
我们还可以总结出来
若两个对象hashCode()返回相同int数,则equals(Object obj)不一定返回true。
若两个对象hashCode()返回不同int数,则equals(Object obj)一定返回false。
同一对象在执行期间若已经存储在集合中,则不能修改影响hashCode值的相关信息,否则会导致内存泄露问题。
如何重写hashCode方法
在编写hashCode时,你需要考虑的是,最终的hash是个int值,而不能溢出。不同的对象的hash码应该尽量不同,避免hash冲
突。
那么如果做到呢?下面是解决方案。
定义一个int类型的变量 hash,初始化为 7。接下来让你认为重要的字段都参入散列运算每一个重要字段都会产生一个hash值,为
最终的hash值做出贡献(影响)最后把所有的分量的总和加起来,注意并不是简单的相加。选择一个倍乘的数字31,参与计算。
然后不断地递归计算, 直到所有的字段都参与了。
说白了:就是让所有的成员变量都参与运算,设计一个复杂的公式,尽可能的减少hash值的碰撞。
HashMap 的常量
/**
* The default initial capacity - MUST be a power of two.
*/
static final