HashMap是Hashtable的轻量级的实现,采用数据结构中的哈希表进行存储,遇到冲突时,采用拉链法解决冲突。
HashMap中有一个Entry[capacity]数组,Entry即为map中一个存储类,其中有成员key,value,next,next指向下一个同义词的Entry。
几个关键的属性
存储数据的数组
transient Entry[] table;
默认容量
static final int DEFAULT_INITIAL_CAPACITY = 16;
最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
默认加载因子,加载因子是一个比例,当HashMap的数据大小>=容量*加载因子时,HashMap会将容量扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
当实际数据大小超过threshold时,HashMap会将容量扩容,threshold=容量*加载因子
int threshold;
加载因子
final float loadFactor;
寻址时,采用key的哈希值(通过一种算法对其hash值进行了运算)model table表的长度,得到相应的值。
插入时,在哈希表中遍历此值对应的链表,如果有相同的key值则替换其value值,如果没有则插入在表头。
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
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++;
addEntry(hash, key, value, i);
return null;
}
indexFor方法,其中table的length初始值为16,最大值为1<<30(2^29),而table的容量扩充是通过左移来实现的,所以h&(length-1)相当于mod length
static int indexFor(int h, int length) {
return h & (length-1);
}
如put代码所示,当hashmap中有此key时,替换掉原来的value,如果没有此key,则添加此Key,调用addEntry(hash,key,value,i)
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);
}
//往table中添加此key,相当于在链表表头插入该元素。(new Entry<>(hash,key,value,e) e参数为next)
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++;
}
HashMap与Hashtable主要的区别在于,HashMap允许空(null)键值(key)和空value值,Hashtable是线程安全的。