它还会有一个哈希运算,就是为了避免哈希冲突。何为哈希冲突?不同的key值(也就是说是不同的对象),最终得到的hash值是相同的。造成锁定到同一个索引位置 。
这里key即是我们传进来的new A(i),通过运行时绑定,可知调用的hashCode方法就是我们重写的
因为A这个类默认是继承Object这个类的。
hashCode方法是属于Object这个java类的,但是当子类重写了这个方法之后,我们每当调用这个方法,编译的时候看父类是否有这个方法,来决定是否编译的时候会报错。
然而运行的时候,是先从子类开始找这个hashCode方法,子类如果有,那么就使用子类的hashCode方法,如果子类没有,那么一层层的往父类,爷类进行寻找这个hashCode方法
接下来分析一下这个put底层的putVal方法
在putVal添加的过程中我们,维护了一个Node
为了把传进来的key,value值封装为一个Node
如果一开始这个节点数组为空,那么我们就要进行扩容操作。
当这个节点数组不为空之后,我们就可以通过计算算出该key对应的索引位置,hash值是通过key对象计算得来的。。。底层也会有一层计算索引的运算法则,如下图:
如果该位置没有节点,那么就直接插入
如果该位置有节点,
我们就要判断该位置已插入的节点和我们待插入的节点是否是相同的。。
如何判断它俩是否是相同的呢?
我们需要满足两个条件:
1.首先hash值必须相同。已插入Node
2.接下来就要判断已插入Node
如果说发现不相同,没关系,对于第二个条件,我们还有一个选择,满足就可以说明已插入的节点和待封装插入的是相同的,也就是key.equals(k)。
这个equals方法是Object类的方法
但是只要我们key对象所对应的类中重写这个equals方法,我们就会优先调用子类中重写父类Object的equals方法【运行时绑定】。所以说,这个equals比较规则可以由我们自己决定。
ok,如果说该equals方法返回true,那么就可以证明出是相同的啦!!
--但是记住,一切的前提就是先满足第一个条件!!
那么就会记录这个p,p在前面确定索引位置的时候已经赋过值了,见上一个图
---------------
小结:
我们知道hash值相同,不一定说明这俩就是同一个对象
但是同一个对象,所求出的hash值肯定是相同的。
所以说,当我们判断出hash值相等的时候,并不能百分比确定它俩就是同一个key对象,不能断定然后直接记录之后value值覆盖
所以说,我们要满足两个条件,
1.已插入Node
2.已插入Node
-----------------
接下来继续
当发现待插入的key对象和该索引位置第一个节点对象不是同一个key对象的时候,我们就要进行情况判断,该索引位置处的节点成树状,那么就要进行树状式添加。
如果是链条形式:
那么就要先遍历整个链条,
依次进行比较已插入的节点对象的hash属性和待封装为Node
比较规则同上
在遍历的过程中, 如果发现下一个索引位置处为null,那么直接封装传进来的key value hash 值封装为一个Node
但是当tab这个我们底层维护的数组的长度大于64,并且该链条长度大于等于8时,我们就要进行树化操作。目的就是为了提高查找性能,优化效率!
OK,结束!