HashMap底层是用一个Entry类型的数组实现的,每个Entry对象的内部又含有指向下一个Entry类型对象的引用,
下面是源码的部分分析,我看你提问,所以我也学习回忆了一下,0-0
static final int DEFAULT_INITIAL_CAPACITY = 16;
transient Entry[] table;
//源码中一个不带参数的构造方法,new了一个数组
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
table = new Entry[DEFAULT_INITIAL_CAPACITY];//构造方法new了一个Entry类型的数组,长度为16,table引用
----------------------------------------------------------
看Entry类型,内部拥有加入Map中的K,V,hash值,和自身的一个引用
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next; ///Entry类型内部有一个自己类型的引用,指向下一个Entry
final int hash;
………………………………………………………………}
//看HashMap的put方法
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);//如果key为空,看下面进入putForNullKey方法
int hash = hash(key.hashCode());//此hash值根据put加入的key的hashCode()值计算出来
int i = indexFor(hash, table.length);//根据key的hash值,和table数组的长度计算出一个i,这个i就是确定了put的K,V放入到底层table数组的哪个位置
// static int indexFor(int h, int length) {
return h & (length-1); }
//如果put的key不为空,下面遍历数组table中的每一个Entry,用Entry的hash值与加入的key值计算的hash值比较,或是直接用Entry的key值equals加入的key值,
如果都满足相等(其实就是根据key的hashCode()和equals方法判断key是否重复),则返回Entry中的旧value,用新加入的value替换, e.value = value;
for (Entry<K,V> e = table; 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++;
addEntry(hash, key, value, i);//put的key值与数组中Entry的key值都不相同时,则new一个新的Entry对象,加入到底层数组中
return null;
}
//看addEntry方法
void addEntry(int hash, K key, V value, int bucketIndex)
{
Entry<K,V> e = table[bucketIndex];//根据indexFor(hash, table.length)计算的值i确定在table数组中产生的新的Entry对象的位置,
把位置引用赋给一个Entry变量,(如果该位置上已经有一个Entry对象,把引用给e)
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);///new了一个新的Entry对象,它站了引用位置table[bucketIndex],
同时内部拥有了e(上一个该table[bucketIndex]位置上Entry对象的引用)
如果new 了一个Entry B 则B在 table[bucketIndex]位置上,同时B内有上一个
Entry对象A的引用,B又指向了A,如果又有一个对象C要加入到数组,位置也在
table[bucketIndex]上,则C又指向了B,看图例:
if (size++ >= threshold)
resize(2 * table.length);
}
//看遍历table中的每一个Entry,如果key为空,则返回对应的旧value,用新put的value替换e.value=value
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;
}
|