HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。不保证映射的顺序,特别是它不保证该顺序恒久不变。
2. HashMap的数据结构:
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
数组中存放的键值对:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
}
Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表
3. HashMap的存取实现:
1.存储
public V put(K key, V value) {
//键值为null,采取特别处理
if (key == null)
return putForNullKey(value);
//计算键值的hash值
int hash = hash(key.hashCode());
//计算 h & (length-1),获得在数组中的序号
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;
}
}
//该位置上没有存储键值对或者不存在相同的key,在该位置上存储键值对
modCount++;
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
//创建键值对,存储在该位置上,其next指向的原来的键值对
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
//实际容量超过预定负载后,把长度扩展一倍
//threshold = (int)(capacity * loadFactor)
//capacity必须为2的幂
//loadFactor默认为0.75,可以在构造函数中制定
if (size++ >= threshold)
resize(2 * table.length);
}
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++;
//null键总是放在数组第一位置上的链表中
2.获取addEntry(0, null, value, 0);
return null;
}
public V get(Object key) {
// 键值为null,采取特别处理
if (key == null)return getForNullKey();
//计算键值的hash值
int hash = hash(key.hashCode());//通过hash,计算出其存储位置的索引,在该位置上遍历链表,取得键值对,返回valuefor (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.equals(k)))return e.value;}return null;}//在数组的第一个位置上遍历链表,查找key为null的键值对
private V getForNullKey() {for (Entry<K,V> e = table[0]; e != null; e = e.next) {if (e.key == null)return e.value;}return null;}
4.确定数组index:hashcode % table.length取模
HashMap存取时,都需要计算当前key应该对应Entry[]数组哪个元素,即计算数组下标