jdk1.7中的HashMap源码
1.函数签名
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
实行了Map接口。
2.静态内部类Entry
,(jdk1.8中叫做Node,都表示节点的意思)
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
有四个关键点:hash
,key
,value
,next
。
3.关键的成员变量
默认的初始容量:16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
最大的容量
static final int MAXIMUM_CAPACITY = 1 << 30
默认的装填因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
阈值:最大的容量*装填因子
底层的Entry
的一个数组的引用
transient Node<K,V>[] table;
Map中节点的个数
transient int size;
4构造方法
把默认的装填因子,赋值给loadFacto
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
这段代码中一直在进行位的的左移,是为了保证capacity
(主数组的长度)的值永远为2的倍数,这与后面计算哈希码有关。
添加操作(put方法)
存储元素分为3步
(1)计算哈希码
这里调用了hash()
方法,该方法中调用了hashcode()
方法
,并且还进行了更加复杂的操作,目的就是为了,使计算出的哈希码不同。
(2)计算存储位置
这里使用的是位运算,目的就是为了提高效率。只有当哈希码的值是二的倍数是,才会让上面的结果与直接对length
取模的结果相同。
(3)添加到链表中
如果size
>=阈值,并且要添加的存储位置不为空,就会对table
进行扩容,扩容为原来的二倍。最终都要创建一个新的节点。如果发现有相同的key
就会用新的value
替代旧的value
,并且返回旧的value
并且会加到链表的头部。
get方法
找value的时候,其实就找的是Entry,因为Entry就包含value和key。
根据key找value
jdk1.8的变化
jdk1.8中依然是一个table
,但如果链表中的节点个数>=8时,就会采用红黑树结构。来提高效率。