>/**> * 传入初始容量大小,使用默认负载因子值 来初始化HashMap对象 >*/ >public HashMap(>int initialCapacity) { >this(initialCapacity, DEFAULT_LOAD_FACTOR); } >/**> * 默认容量和负载因子 >*/ >public HashMap() { >this.loadFactor = DEFAULT_LOAD_FACTOR; >//> all other fields defaulted } >/**> * 传入初始容量大小和负载因子 来初始化HashMap对象 >*/ >public HashMap(>int initialCapacity, >float loadFactor) { >//> 初始容量不能小于0,否则报错 >if (initialCapacity < 0) >throw >new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); >//> 初始容量不能大于最大值,否则为最大值 >if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; >//>负载因子不能小于或等于0,不能为非数字 >if (loadFactor <= 0 || Float.isNaN(loadFactor)) >throw >new IllegalArgumentException("Illegal load factor: " + loadFactor); >//> 初始化负载因子 >this.loadFactor = loadFactor; >//> 初始化threshold大小 >this.threshold = tableSizeFor(initialCapacity); } >/**> * 找到大于或等于 cap 的最小2的整数次幂的数。 >*/ >static >final >int tableSizeFor(>int cap) { >int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; >return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
tableSizeFor(int cap):用位运算找到大于或等于 cap 的最小2的整数次幂的数。比如10,则返回16。
四、put函数
>public V put(K key, V value) { >//> 调用hash(key)方法来计算hash >return putVal(hash(key), key, value, >false, >true); } >final V putVal(>int hash, K key, V value, >boolean onlyIfAbsent, >boolean evict) { Node<K,V>[] tab; Node<K,V> p; >int n, i; >//> 容量初始化:当table为空,则调用resize()方法来初始化容器 >if ((tab = table) == >null || (n = tab.length) == 0) n = (tab = resize()).length; >//>确定元素存放在哪个桶中,桶为空,新生成结点放入桶中 >if ((p = tab[i = (n - 1) & hash]) == >null) tab[i] = newNode(hash, key, value, >null); >else { Node<K,V> e; K k; >//> 比较桶中第一个元素(数组中的结点)的hash值相等,key相等 >if (p.hash == hash && ((k = p.key) == key || (key != >null && key.equals(k)))) >//>如果键的值以及节点 hash 等于链表中的第一个键值对节点时,则将 e 指向该键值对 e = p; >//> 如果桶中的引用类型为 TreeNode,则调用红黑树的插入方法 >else >if (p >instanceof TreeNode) >//> 放入树中 e = ((TreeNode<K,V>)p).putTreeVal(>this, tab, hash, key, value); >else { >//>对链表进行遍历,并统计链表长度 >for (>int binCount = 0; ; ++binCount) { >//> 到达链表的尾部 >if ((e = p.next) == >null) { >//>在尾部插入新结点 p.next = newNode(hash, key, value, >null); >//> 如果结点数量达到阈值,转化为红黑树 >if (binCount >= TREEIFY_THRESHOLD - 1) >//> -1 for 1st treeifyBin(tab, hash); >break; } >//> 判断链表中结点的key值与插入的元素的key值是否���等 >if (e.hash == hash && ((k = e.key) == key || (key != >null && key.equals(k)))) >break; p = e; } } >//>判断要插入的键值对是否存在 HashMap 中 >if (e != >null) { >//> existing mapping for key V oldValue = e.value; >//> onlyIfAbsent 表示是否仅在 oldValue 为 null 的情况下更新键值对的值 >if (!onlyIfAbsent || oldValue == >null) e.value = value; afterNodeAccess(e); >return oldValue; } } ++modCount; >//> 键值对数量超过阈值时,则进行扩容 >if (++size > threshold) resize(); afterNodeInsertion(evict); >return >null; }
在new HashMap() 完成后,若没有put操作,是不会分配存储空间的。
当桶数组 table 为空时,通过扩容的方式初始化 table
查找要插入的键值对是否已经存在,存在的话根据条件判断是否用新值替换旧值
如果不存在,则将键值对链入链表中,并根据链表长度决定是否将链表转为红黑树
判断键值对数量是否大于阈值,大于的话则进行扩容操作
五、hash值的计算
根据存入的key-value对中的key计算出对应的hash值,然后放入对应的桶中,所以好的hash值计算方法十分重要,可以大大避免哈希冲突。