两大常见容器之HashMap是如何实现的?
Posted 刘兆贤
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了两大常见容器之HashMap是如何实现的?相关的知识,希望对你有一定的参考价值。
本文来自刘兆贤的博客_CSDN博客-Java高级,Android旅行,Android基础领域博主 ,引用必须注明出处!
最近计划研究HashMap的内部实现,经过三天时间,终于可以写下一些东西。
数据结构:数组+链表。即每个数组中的单个元素,都是一个链表,该元素的下标由key的hash与数组长度决定,该链接的key的hash都相等。
特点:
- 它和HashTable大致相同(除了非线程安全和允许空键、空值),不保证排列顺序一直不变;
- 不要设置扩容因子过大或者过小,否则超过最大数量后,不再执行hash操作。
- 比较时,不再判断hashCode,而是根据key的hash获得下标。
- iterator方法执行后,如果map再执行修改,则iterator.remove时会报非同步修改错误。
扩容:
- 如果table数量大于0,且大于1<<30,则扩容因子threshold=Integer.MAX_VALUE,用于下次扩容的判断条件,返回table对象不变;
- 如果小于上述最大值,则扩容1倍,即threshold(首次为12=16*0.75)*2=24;(ArrayList默认为10个,扩容0.5倍)
- 如果e.next为null,则直接将e赋值在当前hash&(newCap-1)的下标位;如果是TreeNode,则使用红黑树的split方法;
- 如果e.next不为null,则将此后的对象,分成两个队列(e.next=newE);hash&oldCap==0则放在当前位置j,否则放在新位置(j+oldCap)
hashmap 查找速度:
O(1)+O(lgn),8个以内O(1),以外红黑树O(lgn)。
为什么HashMap扩容1倍?
因为可以减少hash冲突,避免形成链式结构,从而降低查询效率。比如11001&10111=10001 11001&11111=10001
增加/修改:
- 默认生成16个数据的数组
- 根据key的hash,定义下标,从下标1开始存数据,如果不存在则生成新的对象,如果存在则返回已有对象;
- 如果链表超过存储长度,则把链表转变成红黑树进行存储,modCount(修改数量)加1,
- 如果目前个数大于threshold(首次为12=16*0.75),则执行上述扩容操作。
借图一张,以表示插入过程
再借一张,表示数据结构
查找:
- 根据key的hash找到下标,如果key相等则查到一个对象O;
- 否则,如果O是TreeNode,则转成红黑树的查找方式;
- 否则,依次查找O.next,直到key相等;
删除:
先查找到目标对象,然后再删除;
- 如果对象O直接查出,则O赋值为O.next;
- 如果对象O是TreeNode,则按照红黑树的删除方式;
- 如果对象是在next队列中找到,则将O赋值为O.next。
一个疑问:
达到12个时,仅设置p.next,未对table进行赋值,为什么却出现在table里?因为table的值会最终汇总。
红黑树
10亿数据的快速查找方案:建立红黑树(旋转-例左转,则将该结点的右结点,转换成该结点的父结点,将右结点的左结点转成该结点的右结点、自平衡、上色),
查找/删除/插入平均和最差效率为O(logn)。
借图一张,表示从pivot左旋转。
性质:
- 每个结点,要么黑要么红;
- 根结点是黑色;
- 空结点都是黑色;
- 红色结点,两个子结点是黑色;
- 叶结点到空结点路径上,黑结点数量相等。
特点:
- 没有连续的红色结点,但可以有连续的黑色结点,根结点是黑色;
- 当前结点,总比左结点大,比右结点小;
- 最长路径,不超过最短路径的2倍;
三个问题:
- 颜色的出现,是为了实现平衡二叉树的特性。
- 什么情况下,需要变色?两个相邻结点为红色。
- 什么情况下,需要旋转?当变色已经不满足要求时。
位移符号
>>:带符号右移。正数右移高位补0,负数右移高位补1。
例:4 >> 1,结果是2;-4 >> 1,结果是-2。-2 >> 1,结果是-1。
>>>:无符号右移。无论是正数还是负数,高位通通补0。
对于正数而言,>>和>>>没区别。
对于负数而言,-2 >>> 1,结果是2147483647(Integer.MAX_VALUE),-1 >>> 1,结果是2147483647(Integer.MAX_VALUE)。
以下代码可以判断两个数的符号是否相等
return ((a >> 31) ^ (b >> 31)) == 0;
以上是关于两大常见容器之HashMap是如何实现的?的主要内容,如果未能解决你的问题,请参考以下文章
java:容器/集合(Map(HashMap,TreeMap))
java集合之HashMap与ConcurrentHashMap的自我理解
并发容器线程安全应对之道-ConcurrentHashMap