JDK1.7:
get()方法
- 首先判断输入的key是否为空,如果为空,从hashmap数组下标为0的位置获取值返回
- 如果不为空,根据key的值,从hashmap数组中获取对应的entry对象,判断这个对象是否为空,为空返回null,不为空返回对应的value值, 获取value的方法中key为空和不为空时的方法里都先判断数组中的元素是否为0 ,如果不为0,才继续查找
// hashmap的get方法
public V get(Object key) {
// 判断key 是否为null
if (key == null)
// 如果为null 获取key为null的值
return getForNullKey();
// 获取对应key为要查询的key的entry
Entry<K,V> entry = getEntry(key);
// 判断是否获取到entry,如果没有,返回null,如果不为null,返回对应entry的value值
return null == entry ? null : entry.getValue();
}
// 当key为null时获取value的值
private V getForNullKey() {
// 判断hashmap中总的entry的数量,如果为0,说明hashmap中还没有值,返回null
if (size == 0) {
return null;
}
// 如果size 不为0 , 获取entry[] 数组中 下标为0的位置的链表
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
// 如果有entry对应的key的值为null ,返回对应的value
if (e.key == null)
return e.value;
}
// 如果没有,返回空
return null;
}
// 如果key不为null,获取key对应的value
final Entry<K,V> getEntry(Object key) {
// 如果key不为null,判断hashmap中entry的数量是否为0 如果为0 返回null
if (size == 0) {
return null;
}
// 获取key的value值,如果key为null,返回hash值为0,反之,计算key对应的hash值
int hash = (key == null) ? 0 : hash(key);
// 遍历指定下标的entry数组元素链表
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
// 判断key的hash值与entry中的hash值是否相同,并且key通过== 和 equal 比较,
// 都为true时,返回这个 entry 对象
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
// 如果指定下标key中的entry没有满足条件的,返回null
return null;
}
// 计算 hash值
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
// 通过hash值以及数组长度的位运算,获取entry的下标
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
return h & (length-1);
}
put()方法
-
调用put方法的时候首先判断hashmap数组是否为空数组,
-
如果为空,进行初始化,判断key的值是否是null,
-
如果是null,把对应的value值存进数组中下标为0的位置,
-
计算key的hash值,并计算出下标,
-
遍历下标对应的链表,匹配hash值和key的值,
-
如果存在,则覆盖,返回旧值
-
如果不存在,新添加一个,返回null
-
如果扩容,是先扩容之后再把数据添加进新扩容的数组中
如果数组中元素的容量超过阈值,会触发扩容,
扩容是先把源数组放进一个临时数组中,获取老数组的长度,通过老数组长度乘2获取新数组长度,并创建新数组,
把临时数组中的数据通过重新计算下表,存进扩容后的数组中.
// hashmap中put的方法
public V put(K key, V value) {
// 判断 entry[] 数组是否为空数组 如果为空 初始化entry数组
if (table == EMPTY_TABLE) {
// 初始化hashmap
inflateTable(threshold);
}
// 如果key 为null 把这个value对应的entry放进table[0]位置中
if (key == null)
return putForNullKey(value);
// 计算 key的hash值
int hash = hash(key);
// 计算 key对应的 entry所在数组的下标
int i = indexFor(hash, table.length);
// 获取上面计算的下标的链表
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
// 如果存在hash值相同,并且key相同的entry,对value进行覆盖,并返回覆盖区的value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
// 如果链表中没有,则添加
modCount++;
addEntry(hash, key, value, i);
return null;
}
// 初始化 hashmap
private void inflateTable(int toSize) {
// 根据初始化的值,获取对应的小的大于这个值的 2 的n次方的值,也就是hashmap的容量
int capacity = roundUpToPowerOf2(toSize);
// 通过容量与扩容因子的相乘,获取最大不触发扩容的容量
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
// 创建数组
table = new Entry[capacity];
initHashSeedAsNeeded(capacity);
}
// 获取当前数值最接近并且大于当前数值的最小2的n次方
private static int roundUpToPowerOf2(int number) {
// Integer.highestOneBit((number - 1) << 1) 获取当前数值减去1后向左位移1位,并且第二位往后都为0的值
return number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}
// 当key为null的时候,对应value存放的位置
private V putForNullKey(V value) {
// 获取table[0]的entry, 遍历这个链表
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
// 如果存在key为null的entry,把value进行覆盖,并返回覆盖前的value
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
// 如果对应key为null的entry不存在,则在table[0]位置添加一个key为null的entry,并modcount加1
modCount++;
// 添加entry
addEntry(0, null, value, 0);
// 返回null
return null;
}
// 添加 entry 参数是key的hash值,key,value,下标
void addEntry(int hash, K key, V value, int bucketIndex) {
// 判断hashmap中所有entry的数量是否大于扩容临界值并且指定下标处的entry[]数组元素不为null 触发扩容
if ((size >= threshold) && (null != table[bucketIndex])) {
// 扩容后的容量是原先的两倍
resize(2 * table.length);
// 获取key的hash值
hash = (null != key) ? hash(key) : 0;
// 重新计算下标
bucketIndex = indexFor(hash, table.length);
}
// 创建entry
createEntry(hash, key, value, bucketIndex);
}
// 创建entry的方法
void createEntry(int hash, K key, V value, int bucketIndex) {
// 获取指定下标的entry
Entry<K,V> e = table[bucketIndex];
// 新创建的entry作为原先链表的最顶端,覆盖创建前的entry
table[bucketIndex] = new Entry<>(hash, key, value, e);
// 数组内总的entry加1
size++;
}
// 扩容算法
void resize(int newCapacity) {
// 首先把老的数组复制到一个临时数组中
Entry[] oldTable = table;
// 保存老的数组的长度
int oldCapacity = oldTable.length;
// 判断 老的数组长度是否等于最大值 如果等于, 扩容阙值为integer的最大值
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
// 如果不相等,根据新的数组长度创建数组
Entry[] newTable = new Entry[newCapacity];
// 移动老的数组中的数据到新的数组
transfer(newTable, initHashSeedAsNeeded(newCapacity));
// 把table的引用指向新的数组
table = newTable;
// 获取扩容的阙值
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
// 移到老的数组中的数据到新的数组里面
void transfer(Entry[] newTable, boolean rehash) {
// 获取新的数组的长度
int newCapacity = newTable.length;
// 遍历老的entry数组
for (Entry<K,V> e : table) {
// 判断entry不为null
while(null != e) {
// 获取链表中的entry
Entry<K,V> next = e.next;
if (rehash) {
// 获取hash值
e.hash = null == e.key ? 0 : hash(e.key);
}
// 通过hash与新的数组长度,获取key在新的数组中的下标
int i = indexFor(e.hash, newCapacity);
// 当前entry添加到数组之前,先原来的entry存进当前entry下
e.next = newTable[i];
// 把当前entry赋给entry数组
newTable[i] = e;
// 对当前entry链表的下一个entry进行赋值
e = next;
}
}
}
JDK1.8
get方法
对输入的key的值计算hash值,
首先判断hashmap中的数组是否为空和数组的长度是否为0,如果为空和为0,则直接放回null
如果不为空和0,计算key对应的数组下标,判断对应位置上的第一个node是否满足条件,如果满足条件,直接返回
如果不满足条件,判断当前node是否是最后一个,如果是,说明不存在key,则返回null
如果不是最后一个,判断是否是红黑树,如果是红黑树,则使用红黑树的方式获取对应的key,
如果不是红黑树,遍历链表是否有满足条件的,如果有,直接放回,否则返回null
public V get(Object key) {
Node<K,V> e;
// 计算hash值,与key一起找key的值
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
调用 getnode方法
/**
* Implements Map.get and related methods
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
// 设置一些局部变量
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
// 首先获取hashmap中的数组和长度,并判断是否为空,如果为空,返回null
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
// 获取key对应的下标对应的链表对象, 并比较第一个是否满足条件
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
// 第一个如果满足条件,则直接返回
return first;
// 判断当前对象是否是最后一个,如果是,说明没有找到对应的key的值
if ((e = first.next) != null) {
// 如果不为空,判断是否是红黑树,如果是红黑树,使用红黑树获取对应key的值
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
// 如果不是红黑树, 遍历链表,找到对应hash和key的node对象
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
public V put(K key, V value) {
// 获取key的value值,调用用putval方法
return putVal(hash(key), key, value, false, true);
}
1.8 hashmap中put方法
首先计算key的hash值,获取hashmap中的数组和数组长度,如果数组为空,初始化计算key的下标
数组对应下标的位置是否为空,如果为空,则先添加一个,放在这个下标位置,然后判断数组内元素是否大于阈值,如果大于,则进行扩容
如果数组对应下标不为空,则先获取对应链表的第一个值,判断hash和key是否相同,如果相同,新value替换旧value,返回旧value
如果第一个值key不相同,判断当前链表是否是红黑树,如果是红黑树,调用红黑树链表put的方法
如果也不是红黑树,遍历链表,判断当前node是否是最后一个,如果是,说明链表中没有新添加的key,则在最后面新添加一个,然后判断是否超过阈值(8-1),如果超过,则转换成红黑树
如果不是最后一个,说明在中间已经存在key了, 把新值赋值给旧值,并返回旧值,判断是否需要扩容.
/**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 首先创建局部变量并进行赋值,获取当前数组的长度
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// 判断key对应下标的数组位置是否为空,如果为空,在这个下标位置创建一个
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
// 如果key对应的参数不为空
Node<K,V> e; K k;
// 判断新添加的对象与旧对象中第一个对象的key的hash值和key是否相等
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
// 如果相等, node值存进临时值
e = p;
else if (p instanceof TreeNode)
// 如果 已经存在的node对象是 treennode(红黑树)的实现类, 调用treenode的putval方法存
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// 如果新添加的key不遇第一个node中的key相同,并且不是treenode的实现类,遍历node链表
for (int binCount = 0; ; ++binCount) {
// 判断当前node是否是最后一个node,如果是,把新添加的node添加进这个链表中
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
// TREEIFY_THRESHOLD 默认是8, 即判断当前链表的长度是否大于7,如果是,则把链表转换成红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
// 执行到最后一个后,跳出循环
break;
}
// 如果当前node不是最后一个,比较key和hash值,如果存在相同,跳出循环(此时已经找到链表中key对应的node)
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
// 下一个node返回
p = e;
}
}
// 判断找到的node是否为空,如果不为空
if (e != null) { // existing mapping for key
// 获取旧值
V oldValue = e.value;
// 如果设置是否可以改变值,如果设置不能改,但是旧值为空,则可以改,否则,需要设置可以改值的情况下才可以把新值赋给旧值
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
// 返回旧值
return oldValue;
}
}
++modCount;
// 判断当前数量是否超过扩容阈值,如果超过,进行扩容, 与1.7先扩容再添加不同,1.8是先扩容再添加
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}