一、HashMap底层用到的数据结构
数组+单向链表+红黑树
数组:数组每一项都是一个链表,其实就是数组和链表的结合体
单向链表:当法神hash碰撞时,首先会找到数组对应位置,然后1.8采用尾插入法(1.7采用头插入法),形成一个单项链表结构
JDK1.8 红黑树:当数组中每项的链表长度大于8时,会转换为红黑树
二、什么是hash碰撞?解决方案?
hash碰撞:不同的key可能会产生相同的hash值;
方案:链表发,再哈希法;
hashMap中采用链表发,在ConcurrentHashMap中采用哈希法;
二、红黑树与二叉树比较
二叉查找树在特殊情况下也会变成线性结构,和原来链表有共同的问题,节点太深,查找性能慢;
红黑树相比二叉树,在检索的时候效率其实差不多,都是通过平衡来二分查找。但对于插入删除等操效率提高很多。红黑树不像二叉树一样追求绝对的平衡,它允许局部很少的不完全平衡,这样对于效率影响不大,但省去了很多没有必要的调平衡操作,二叉树调平衡有时候代价较大,所以二叉树的效率不如红黑树;
三、为什么采用红黑树
在平常我们用HashMap的时候,HashMap里面存储的key是具有良好的hash算法的key(比如String、Integer等包装类),冲突几率自然微乎其微,此时链表几乎不会转化为红黑树,但是当key为我们自定义的对象时,我们可能采用了不好的hash算法,使HashMap中key的冲突率极高,但是这时HashMap为了保证高速的查找效率,就引入了红黑树来优化查询了。
四、为什么临界值为8
通过源码我们得知HashMap源码作者通过泊松分布算出,当桶中结点个数为8时,出现的几率是亿分之6的,因此常见的情况是桶中个数小于8的情况,此时链表的查询性能和红黑树相差不多,因为转化为树还需要时间和空间,所以此时没有转化成树的必要。
当数据较少的时候,采用链表要比红黑树效率高,因为平衡二叉树保持平衡需要耗费资源,那么前期数据较少时采用链表,当链表中的数据长度大于8时,就将链表转换成红黑树,可以加快数据的插叙速度,官方测试8为性能最优。
五、put()底层分析
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * Implements Map.put and related methods * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; //判断当前数组是否为空,如果为空要进行第一次扩容 if ((tab = table) == null || (n = tab.length) == 0) //扩容后将扩