本文共 1515 字,大约阅读时间需要 5 分钟。
HashMap的工作原理分析
HashMap并不算特别神奇,它的工作原理相对来说还是比较直接的。以下是我对HashMap的一些理解和分析。
数据结构方面
在Java中,常用的数据结构包括数组、链表和树。数组的查询效率很高,链表的新增效率又很高。HashMap的数据接口将这两种数据结构的优势结合了起来。
数组的实现
数组在Java中用数组表示,代码如下:
int[] aa = new int[16];
其中,16是2的4次方(1左移4位),这是数组的默认初始大小。这里用了位移运算来确定数组的大小,而不是直接写16,这样既方便又高效。
链表的实现
链表的实现方式类似于下面的代码:
public class Node { Object data; Node next; }
双向链表的实现方式类似于:
public class Node { Object data; Node previous; Node next; }
HashMap的独特之处
HashMap的独特之处体现在以下几个方面:
HashMap并不是单纯的数组或链表,而是两者的结合。数组用于存储元素,链表用于解决碰撞问题。
数组的初始大小是16,这是2的4次方。这种选择有两个好处:
为什么不直接写16呢?因为底层都是二进制,位移运算效率更高。
链表的长度限制
链表的长度是有上限的。具体来说:
这种设计目的是为了在高频率的删除操作后,避免链表过长导致效率低下。
put方法分析
put操作分为以下几个步骤:
hash函数的实现
hash函数的实现非常关键。具体来说:
int hash = key.hashCode(); int hash = hash ^ (hash >> 16);
这个hash函数的作用是将高16位和低16位进行异或运算,从而得到一个较为分散的hash值。
为什么要用异或运算?
异或运算(^)的特点是:
这样可以充分利用高低位,提高hash值的分散性。
数组存储位置的确定
存储位置的计算公式是:
int index = (n - 1) & hash;
其中,n是数组的大小。n-1的作用是确保在碰撞发生时,仍然能得到有效的数组索引。
为什么不直接用n?
因为n是2的幂次方,n-1可以用来提取有效位。例如:
n = 16 → n-1 = 15 → 0b1111
这样无论hash值是什么,与运算n-1后,结果都落在0到15之间。
这种方法比模运算(%)更高效,因为模运算需要进行额外的计算。
链表转换与红黑树
当链表长度超过8时,HashMap会将链表转换为红黑树。这是因为红黑树的查询效率更高。
碰撞处理
如果多个元素的hash值相同,就会形成碰撞。此时,HashMap会自动将链表转换为红黑树,或者在删除操作频繁时强制转换为链表。
模运算与按位与运算
模运算(%)和按位与运算(&)的区别在于:
模运算效率较低,所以HashMap采用按位与运算来提高效率。
总结
HashMap的工作原理主要包括以下几个方面:
这种设计既保证了哈希表的平均时间复杂度,又在面对频繁碰撞时能够灵活调整结构,确保整体性能。
转载地址:http://yxpmz.baihongyu.com/