public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
首先HashMap 继承自AbstractMap(抽象类) 实现了Map接口。
在new HashMap<K, V>()时,通过底层代码可以知道:
public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); }
其中loadFactor为float型,指hash表的负载因子;DEFAULT_INITIAL_CAPACITY 为常量,是hash表的初始容量,初值为16;threshold为int型,是hash表的装载阈值,反应了hash表的装载程度。接下来就会初始化一个Entry类型的数组,初始容量为16;而其中table的类型Entry是Hashmap的一个静态内部类。
static class Entry<K,V> implements Map.Entry<K,V>,并且该类没有无参构造函数, Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; }
当使用hashMap.put(K,V)时,
public V put(K key, V value) { if (key == null) //判断键是否为null return putForNullKey(value); //添加键为null的值 int hash = hash(key.hashCode()); //计算二次计算key的hash值,避免冲突 int i = indexFor(hash, table.length); //根据hash值确定元素存放在表中的索引 for (Entry<K,V> e = table[i]; e != null; e = e.next) { //定位到table中的位置后,取出当前的链表 Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { //判断key是否重复 V oldValue = e.value; //重复的时候返回重复key的value值 e.value = value; e.recordAccess(this); //这句的作用是什么不知道 return oldValue; } } modCount++; // modCount是不可被序列化的,具有可见性的, //用来记录hashmap的修改次数 addEntry(hash, key, value, i); //添加一个键值对 return null; }
void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); if (size++ >= threshold) //当hash表的大小>=阈值时进行扩容,hash表的大小变为原来的两倍。 resize(2 * table.length); } Entry(int h, K k, V v, Entry<K,V> n) { value = v; //直接让原来table[bucketIndex]位置上的表头为新元素的next值,说明,每次插入元素都会在表位置中的表头。 next = n; key = k; hash = h; } void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) {//当hash表的容量到达最大值时不再扩容 threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable); //将原来hash表中的内容复制到新的hash表中 table = newTable; threshold = (int)(newCapacity * loadFactor); }
Hashmap中只允许有一个空键的原因:
从代码中可以看出,当第二次添加空键的时候,会将原来null所对应的值覆盖,而且,空键所对应的key存放在表的第一个位置table[0].通过addEntry(0, null, value, 0),在第一次添加空键元素的时候,可以保证null所对应的元素在table[0]链表的表头。
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; }
原文地址:https://www.cnblogs.com/wishboy/p/9529998.html
时间: 2024-10-10 12:22:18