源码分析 - JDK 1.7
//1. HashMap 的 K,V的值,在创建对象的时候确定,比如:K:Integer V: Stringpublic class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable{//重要属性:定义了一个16,赋给初始化数组长度static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16//定义了一个很大很大的数static final int MAXIMUM_CAPACITY = 1 << 30;//定义了加载因子,为 0.75static final float DEFAULT_LOAD_FACTOR = 0.75f;//定义空的主数组static final Entry<?,?>[] EMPTY_TABLE = {};//添加的元素的数量transient int size;//定义个变量,没赋值,为0 。 --- 这个变量用来表示数组扩容的边界值,int threshold;//加载因子,这个变量用来接收 加载因子的值final float loadFactor;}
构造器
//空构造器public HashMap() {//this.(16,0.75)this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);}//有参的构造器, 16, 0.75 ---------》 不建议使用者调用这个构造器public HashMap(int initialCapacity, float loadFactor) {if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);//确定了加载因子,0.75this.loadFactor = loadFactor;//扩展数组的边界值threshold = initialCapacity;init();}
PUT方法
public V put(K key, V value) {if (table == EMPTY_TABLE) {inflateTable(threshold);}if (key == null) // 对空值进行判断,允许key的值为空return putForNullKey(value);int hash = hash(key); // 获取hash码int i = indexFor(hash, table.length);//得到元素在数组中的位置// 通过放入的数组上,没有元素的话,那么直接添加元素,不走这个for循环。//如果e!= null 满足,就证明这个位置上有元素,for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;//发生哈希碰撞的时候,会先比较哈希值,// 比较key是否是一个对象,如果是,equals就不比较了// 比较key是否是一个对象,如果不是,则比较equals// 如果hash值一样,equals方法比较的结果也一样,那么才会走这个方法if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;// 新value 替换 老 value, 只替换value,不替换valuee.recordAccess(this);return oldValue;// 将old 的value返回,即原始的value值}}addEntry(hash, key, value, i);return null;}// 获取Key 的 hashcode 的方法final int hash(Object k) {int h = hashSeed;if (0 != h && k instanceof String) {return sun.misc.Hashing.stringHash32((String) k);}//进行了二次散列,没有直接用hashcode的值性,减少冲突h ^= k.hashCode();//扰动函数,核心思想:增加hashcode的不确定h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}static int indexFor(int h, int length) {// 计算位置对应的公示,等效于 h % length取余数,因为这样效率高return h & (length-1);}//添加元素的方法void addEntry(int hash, K key, V value, int bucketIndex) {//如果这个size 》= threshold ,这个if不走了if ((size >= threshold) && (null != table[bucketIndex])) {resize(2 * table.length);hash = (null != key) ? hash(key) : 0;bucketIndex = indexFor(hash, table.length);}//创建一个Entry对象createEntry(hash, key, value, bucketIndex);}//创建一个Entry对象void createEntry(int hash, K key, V value, int bucketIndex) {//将下标位置上的元素给eEntry<K,V> e = table[bucketIndex];//封装对象,将这个对象给table[bucketIndex] -----------》 链表的《头插法》table[bucketIndex] = new Entry<>(hash, key, value, e);//元素数量+1size++;}
扩容resize
//添加元素的方法void addEntry(int hash, K key, V value, int bucketIndex) {//如果这个size 》= threshold ,这个if不走了if ((size >= threshold) && (null != table[bucketIndex])) { // 如果size 大于 12(16*0.75) 并且 该位置有元素了,才会去扩容resize(2 * table.length); // 扩容大为2倍hash = (null != key) ? hash(key) : 0;bucketIndex = indexFor(hash, table.length);}//创建一个Entry对象createEntry(hash, key, value, bucketIndex);}void resize(int newCapacity) {Entry[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity == MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return;}Entry[] newTable = new Entry[newCapacity];transfer(newTable, initHashSeedAsNeeded(newCapacity)); // transfer 将新数组给老数组table = newTable;threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);}
