HashMap是一个数据结构 实现Map接口 复杂度O(1) 线程非安全 key和value都可以为空
以jdk7为例说明:
HashMap map=new HashMap();
在实例化以后 底层创建了长度是16的一维数组Entry[ ] table.
...可能已经执行过多次put
map.put(key1value1);
首先 调用key1所在类的hashCode()计算key1哈希值 此哈希值经过某种算法以后得到在Entry数组中的存放位置
如果此位置上的数据为空 此时的key-value1添加成功
如果此位置上的数据不为空 (意味着此位置上存在一个或多个数据(以链表形式存在性能O(n))) 比较key1和已经存在的一个或者多个数据的哈希值 :
如果key1的哈希值与已经存在的数据的哈希值都不相同 此时key1-value1添加成功。
如果key1的哈希值和已经存在的某个数据的哈希值相同 继续比较 调用key1所在类的equals()方法。比较:
如果equals()返回false:此时key1-value1添加成功
如果equals()返回true:使用value1替换value2添加成功
补充:关于情况2和情况3:此时key1-value和原来的数据以链表的方式存储
在不断地添加过程中 会涉及到扩容问题 默认的扩容方式 扩容为原来的2倍 并将原有的数据复制过来
HashMap 数据结构底层是一个数组 通过hash算法来进行散列 根据key通过hash散列将key和value散列到比如位置2
算法是否高级 是否可以降低碰撞
如果有碰撞 eg:2中存放了Entry(key,value,Entry next)解决碰撞的方法有俩种:
1.数组位置下挂载单向列表法:如果再来会往下压 形成链表
2、线性探测法:检测是否碰撞 碰撞之后查找周边 index+1到第三个位置 如果为空 就塞进去 (1.7及以下冲突的解决办法)
hash冲突概率很小 原因如下:
hash算法 将hashCode重写
1、 将数组长度length通常定义为2的指数 会与hashCode值进行取余运算 hashCode(key)与运算length-1
2、hash本身的大量位移操作来降低冲突概率
jdk8相较于jdk7底层实现方面的不同:
1.new HashMap():底层没有创建一个长度为16的数组
2.jdk8底层的数组是:Node[ ] 而非Entry[ ]
3.首次调用put()方法时 底层创建长度为16的数组
4.jdk7底层结构只有数组+链表 jdk8中底层结构: 数组+链表+红黑树。
当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时
此时索引位置上的所有数据改为红黑树存储。