//put大概的的实现思路 put(key,value) { int hashCode=key.hashCode(); int index=hashCode%table.lengtj; //Entr(key,value,next) //这里说得这些都是指对象的应用 就是把两个index一样的存在同一个地址 table[index]=new Entry(key,value,table[index]); //将table[index]插入到头节点
}
初始容量为2的n次幂的原因:
1、方便我们进行与(&)运算
2、扩容时,在进行位运算的时候方便我们进行移动
JDK1.7下 HashMap的put源码
1 2 3 4 5
/** * The load factor used when none specified in constructor. */ staticfinalfloat DEFAULT_LOAD_FACTOR = 0.75f; //加载因子,比如有一个水桶,你一直往里面加水,是满了之后再给一个新的桶,还是说快满的时候换桶,其实在很多使用内存空间的地方都会用到这个,并不是每次达到某个某个内存的length之后才进行溢写;这个值就是控制什么时候开始扩容,因为扩容需要时间的,这个0.75是经过大量的数据和测试得出的,即时间上和空间上的一个权衡
/** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value){ if (table == EMPTY_TABLE) { inflateTable(threshold); //初始化数组 } if (key == null) return putForNullKey(value); int hash = hash(key); //这里也不只是简单的取hashCode 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))) { //相同的key的情况下会覆盖,然后返回旧的数据 V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } }
/** * Retrieve object hash code and applies a supplemental hash function to the * result hash, which defends against poor quality hash functions. This is * critical because HashMap uses power-of-two length hash tables, that * otherwise encounter collisions for hashCodes that do not differ * in lower bits. Note: Null keys always map to hash 0, thus index 0. */ finalinthash(Object k){ int h = hashSeed; if (0 != h && k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); }
h ^= k.hashCode(); //如果两个hashCode的二进制,只有一位是不同的,那么直接调用indexFor做h & (length-1)运算后,那么计算出来的下标就是一样的 // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). //让key的hashCode的二进制中高位参与到运算中来,减少数组下标一致导致的链表过长的问题 //即让生成的hash值更散列一点 h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
/** * Offloaded version of put for null keys * 如果key为null的话,这个值会被存在hashmap的第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); returnnull; }
/** Integer 的方法 * Returns an {@code int} value with at most a single one-bit, in the * position of the highest-order ("leftmost") one-bit in the specified * {@code int} value. Returns zero if the specified value has no * one-bits in its two's complement binary representation, that is, if it * is equal to zero. * * @return an {@code int} value with a single one-bit, in the position * of the highest-order one-bit in the specified value, or zero if * the specified value is itself equal to zero. * @since 1.5 */ publicstaticinthighestOneBit(int i){ // HD, Figure 3-1 i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1); }
/** * 计算数组的下标 h:哈希值;length:数组的length * Returns index for hash code h. */ staticintindexFor(int h, int length){ // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length-1); }
/** * Adds a new entry with the specified key, value and hash code to * the specified bucket. It is the responsibility of this * method to resize the table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ voidaddEntry(int hash, K key, V value, int bucketIndex){ //threshold:预值 =table.length*负载因子 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); }
/** * Like addEntry except that this version is used when creating entries * as part of Map construction or "pseudo-construction" (cloning, * deserialization). This version needn't worry about resizing the table. * * Subclass overrides this to alter the behavior of HashMap(Map), * clone, and readObject. */ voidcreateEntry(int hash, K key, V value, int bucketIndex){ Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); size++; }
/** * Rehashes the contents of this map into a new array with a * larger capacity. This method is called automatically when the * number of keys in this map reaches its threshold. * * If current capacity is MAXIMUM_CAPACITY, this method does not * resize the map, but sets threshold to Integer.MAX_VALUE. * This has the effect of preventing future calls. * * @param newCapacity the new capacity, MUST be a power of two; * must be greater than current capacity unless current * capacity is MAXIMUM_CAPACITY (in which case value * is irrelevant). */ voidresize(int newCapacity){ Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; }
// disable alternative hashing if -1 if (threshold == -1) { threshold = Integer.MAX_VALUE; }
if (threshold < 0) { thrownew IllegalArgumentException("value must be positive integer."); } } catch(IllegalArgumentException failed) { thrownew Error("Illegal value for 'jdk.map.althashing.threshold'", failed); }