版权声明:本文为博主原创文章,转载请注明出处:http://blog.jerkybible.com/2017/07/24/Java源码之HashMap/
访问原文「Java源码之HashMap」
关于HashMap
相信同学们在Coding(搬砖)的过程中经常会用到HashMap,那么HashMap到底是个什么东东呢?
以JDK8来讲,HashMap继承Map接口,在Map接口中描述Map是“An object that maps keys to values. A map cannot contain duplicate keys each key can map to at most one value.” 也就是说Map是一个映射键值的对象。映射中不能包含重复键,并且每个键可以映射到最多一个值
。HashMap实现了Map接口,并且为基本操作(如:get、put)提供了常量时间的性能。
HashMap主要成员变量
下面的代码注释里详细的说明了HashMap的几个重要的成员变量。从这几个变量中可以分析得出,所有的键值对都存放在table
变量中,而table
变量的大小不是固定不变的,是在动态变化的,而且大小始终为2的幂。
|
|
从上面的代码总可以看到,table
属性的类型为Node
,这个Node
实现了Map.Entry
接口,所以本质上还是Map.Entry
。另外,HashMap包含另外一种TreeNode
,TreeNode
本身为红黑树结构。在HashMap中,当一个普通Node(Plain Node)的长度大于一定值时会转换为TreeNode
,而TreeNode
长度小于一定值时会转换普通Node。
从下面的代码中可以看到,Node
实现了Map.Entry
接口,并且本身为链表结构,这样是为了处理不同键值可能会出现相同Hash的情况。
|
|
HashMap中的get方法
get方法是我们常用的方法之一,下面是这个方法的源码。这个方法非常简单,可以看到它是委托给了getNode
方法。
下面是getNode方法,已经在主要代码上面加上了注释,比较好理解。
HashMap中的put方法
put方法也是我们常用的方法之一,下面是这个方法的源码。这个方法也非常简单,可以看到它是委托给了putVal
方法。
下面是putVal
方法,方法比getNode
复杂不少,但是整体思路也是很简单的。简单描述就是首先查找时候已经存在键为key的映射,如果存在的话,判断是否允许替换旧值,如果允许则替换;如果不存在键为key的映射,则新建一个这个映射,如果存在hash冲突,则加载对应hash列表的最后。最后判断map的大小是否大于需要调整大小的阈值,大于的话进行resize
。
HashMap中的resize方法
HashMap中很重要的一个函数就是resize
,代表HashMap容量的调整。首先是在首次往HashMap中放入数据的时候需要初始化table大小,另外是在map中映射数量大于threshold
的时候进行容量调整。在具体的调整过程中,分为树形列表调整和普通单链表的调整。详细请看下面的注释。
|
|
### 小结一下
本篇文章详细介绍了HashMap的相关内容,包含主要成员变量和主要成员方法。但是可以看到的是,HashMap的Node分为普通单链表和树链表,只介绍的单链表,而有一些操作是委托给树链表解决的。首先要说明的是,如果key的hash分散性很好,基本不会将单链表转换为树链表;另外树链表是红黑树,属于比较复杂并且可以单独拿出讲,或者说比较独立不放在HashMap里讲也可以。有时间的话我会对红黑树这块做一个专门的讲解。