HashMap原理(Java)

HashMap能够快速的存取键值对,其查找算法复杂度在没有hash冲突的情况下始终为O(1),不会随着元素数量增多而增加。下面看一下其算法实现。

HashMap里面有一个长度为16的数组table,数组元素类型为Node<k, v>(继承自Map.Entry<k, v>),每个node除了包含key和value外,还保存下一个node的引用(若没有下一个node则为null),这样就能应对hash冲突——当冲突产生时,table的元素node是一个链表结构的链表头。

当调用put存入一个键值对<k, v>时,先计算k的hashcode获得一个整数值,然后用这个整数值计算此键值对对应的node在数组table中的index位置。然后查看table此处是否有元素存在,若不存在直接存到此位置,若存在就遍历以这个node开头的链表,查看是否有和k equery的key对应的node存在,存在则替换,不存在就让k对应的node为新的链表头,原来的链表头作为此新链表头node指向的下一个元素。

当调用get查找时,会先计算k的hashcode,然后计算table中的index位置。然后查找此位置的链表,直到找到和k equery的key对应的node,返回这个node的value。

HashMap中table的长度可以在构造函数中指定,另外还有一个比例因子来减少膨胀增加查找效率。当map中的键值对数量多于table.length * 比例因子时,table的长度会扩充一倍,然后重新计算每个元素在table中的index位置。形成新的hash表。

怎么有k的hashcode计算index坐标?java1.8中的做法是,hashcode(int类型,32位)的高16位和低16位进行按位异或。异或结果(16位)和table.length-1进行按位与运算(其实等价于异或结果除以table.length取余数,但是按位与更快),结果就是index值。

进行查找的时候先找到index,再遍历链表。如果hash冲突很多链表很长,遍历链表这一步就会耗时。java1.8中默认当链表长度大于8的时候,就采用红黑树来存储这些冲突的键值对,查找速度比链表快。

猜你喜欢

转载自blog.csdn.net/xuejianbest/article/details/80392576