版权声明:本文为博主原创文章,转载请注明作者与出处,http://blog.csdn.net/lixingtao0520 https://blog.csdn.net/lixingtao0520/article/details/83342301
HashMap是基于数组和链表来存储键值对对象的,我们简单看下get和put方法的源码。
1、我们调用put方法来存储键值对时,它先调用hash方法来计算hashcode,然后用hashcode和Entry数组的大小来做按位与操作,求出所在的Entry数组的下标位置。通过key与下标所在的Entry链表进行判断是否已经存在此Key,如果存在就重新设置value为新的值,不存在则加入到Entry链表中。加入链表是加入到原头结点之前,如下源码。如果key为null时,特殊处理,将null放到了第一个Entry链表中。
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
//计算hashcode
int hash = hash(key);
//计算所在Entry数组(table)的下标
int i = indexFor(hash, table.length);
//判断是否存在此Key,存在就替换value,并返回新值。
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//modCount值增加,此值是iterator迭代时用来判断是否有其他线程对数据做过修改,如果做过修改则抛出ConcurrentModificationException
modCount++;
//走到这,说明此key不存在于HashMap中,增加节点。
addEntry(hash, key, value, i);
return null;
}
//增加节点方法,首先判断是否需要扩容,如果需要扩容则重新计算hash
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
//创建节点方法,将Entry链表的头结点取出,将新Entry指向原来的头结点,table对应的下标指向新的头结点。
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
//Entry的构造方法。
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
//如果是null值,直接放到第一个Entry链表中。
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
2、当调用get方法获取key时,对于null值特殊处理,直接从table[0]中获取。否则,计算key的hash值,找到key所在的Entry链表,遍历此链表找出对应的key值。
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
//计算hash,遍历对应的链表。
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
//null值从第一个Entry链表中获取。
private V getForNullKey() {
if (size == 0) {
return null;
}
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}