/** * The default initial capacity - MUST be a power of two. * 默认的初始容量-必须是2的幂。 */ staticfinalint DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/** * The maximum capacity, used if a higher value is implicitly specified * by either of the constructors with arguments. * MUST be a power of two <= 1<<30. * 最大容量,当两个构造函数的参数隐式指定较大的值时使用。 * 必须是2的幂<= 1<<30。 */ staticfinalint MAXIMUM_CAPACITY = 1 << 30;
/** * The load factor used when none specified in constructor. * 在构造函数中未指定时使用的负载系数。负载因子 */ staticfinalfloat DEFAULT_LOAD_FACTOR = 0.75f;
/** * The bin count threshold for using a tree rather than list for a * bin. Bins are converted to trees when adding an element to a * bin with at least this many nodes. The value must be greater * than 2 and should be at least 8 to mesh with assumptions in * tree removal about conversion back to plain bins upon * shrinkage. * 使用树而不是列表的容器计数阈值。当向至少有这么多节点的容器中添加元素时,容器被转换为树。 * 该值必须大于2,并且应该至少为8,以与关于在收缩时转换回普通bins的树移除假设相匹配。 * 简单来说就是界定了什么时候转变为树状结构 */ staticfinalint TREEIFY_THRESHOLD = 8;
/** * The bin count threshold for untreeifying a (split) bin during a * resize operation. Should be less than TREEIFY_THRESHOLD, and at * most 6 to mesh with shrinkage detection under removal. * 在调整大小操作期间取消树状(分裂)仓的堆数阈值。应小于TREEIFY_THRESHOLD,且最多为6,以去除收缩检测下的网格。 * 用人话来讲就是当桶的节点数小于这个值会从树转为链表 */ staticfinalint UNTREEIFY_THRESHOLD = 6;
/** * The smallest table capacity for which bins may be treeified. * (Otherwise the table is resized if too many nodes in a bin.) * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts * between resizing and treeification thresholds. * 容器可以树形化的最小表容量。(否则,如果一个容器中有太多节点,则会调整表的大小。)应该至少为4 * TREEIFY_THRESHOLD,以避免调整大小和树形化阈值之间的冲突。 * 用人话说就是当桶大于64就会变成红黑树(条件的一种)另一方面也说明了红黑树的桶最小是64 */ staticfinalint MIN_TREEIFY_CAPACITY = 64;
/** * The table, initialized on first use, and resized as * necessary. When allocated, length is always a power of two. * (We also tolerate length zero in some operations to allow * bootstrapping mechanics that are currently not needed.) * 表,第一次使用时初始化,并根据需要调整大小。当分配时,长度总是2的幂。(在某些操作中,我们也允许长度为零,以允许当前不需要的引导机制。) * 说人话,这就是所谓的桶了,分配时,总长度总是2的幂 */ transient Node<K,V>[] table;
/** * Holds cached entrySet(). Note that AbstractMap fields are used * for keySet() and values(). * 保存缓存的entrySet()。注意,keySet()和values()使用了AbstractMap字段。 * 迭代器所使用的数值 */ transient Set<Map.Entry<K,V>> entrySet;
/** * The number of key-value mappings contained in this map. * 此映射中包含的键-值映射的数量。不过这个size并不代表桶(数组)的长度 */ transientint size;
/** * The number of times this HashMap has been structurally modified * Structural modifications are those that change the number of mappings in * the HashMap or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the HashMap fail-fast. (See ConcurrentModificationException). * 结构修改是指改变HashMap中的映射数量或修改其内部结构(例如,rehash)。该字段用于使HashMap的集合视图上的迭代器快速失败。(见ConcurrentModificationException)。 * 和之前我在ArrayList文章里讲的一样,简单理解为程序计数器,在使用迭代器时,多线程修改数据会报错 */ transientint modCount;
/** * The next size value at which to resize (capacity * load factor). * 要调整大小的下一个大小值(容量*负载因子)。也是一个临界值,当实际大小超过临界值时就会扩容 * * @serial */ // (The javadoc description is true upon serialization. // Additionally, if the table array has not been allocated, this // field holds the initial array capacity, or zero signifying // DEFAULT_INITIAL_CAPACITY.) int threshold;
/** * The load factor for the hash table. * 哈希表的加载因子。这个加载因子就是我们在文章开头讲到的,越大就越密集。 * * @serial */ finalfloat loadFactor;
这里面的值有几个比较关键
loadFactory加载因子:就如文章一开始所言,太大导致查找元素效率低,太小导致数组利用率低,所以默认值DEFAULT_LOAD_FACTOR=0.75这是官方给出的一个比较号的临界值。static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16默认容量为16,也就是说当数据量达到了16*0.75=12的时候会进行扩容。扩容是非常消耗性能的操作,因为扩容会改写所有数据的Hash值。我们使用时应尽量避免扩容
/** * Basic hash bin node, used for most entries. (See below for * TreeNode subclass, and in LinkedHashMap for its Entry subclass.) * 基本hash bin节点,用于大多数条目。(参见下面的TreeNode子类和LinkedHashMap的Entry子类。) */ staticclassNode<K,V> implementsMap.Entry<K,V> { finalint hash; final K key; V value; Node<K,V> next;
publicfinal V setValue(V newValue){ V oldValue = value; value = newValue; return oldValue; }
publicfinalbooleanequals(Object o){ if (o == this) returntrue; if (o instanceof Map.Entry) { Map.Entry<?,?> e = (Map.Entry<?,?>)o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) returntrue; } returnfalse; } }
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and load factor. * *构造一个空的<tt>HashMap</tt>,具有指定的初始容量和负载因子。 * @param initialCapacity the initial 初始容量 * @param loadFactor the load factor 负载因子 * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ publicHashMap(int initialCapacity, float loadFactor){ if (initialCapacity < 0) thrownew IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) thrownew IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); }
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and the default load factor (0.75). * *构造一个空的<tt>HashMap</tt>,具有指定的初始容量和默认的负载因子(0.75)。 * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ publicHashMap(int initialCapacity){ this(initialCapacity, DEFAULT_LOAD_FACTOR); }
/** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). *构造一个空的<tt>HashMap</tt>,默认初始容量(16)和默认负载因子(0.75)。 */ publicHashMap(){ this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
/** * Constructs a new <tt>HashMap</tt> with the same mappings as the * specified <tt>Map</tt>. The <tt>HashMap</tt> is created with * default load factor (0.75) and an initial capacity sufficient to * hold the mappings in the specified <tt>Map</tt>. **构造一个新的<tt>HashMap</tt>,其映射与指定的<tt>Map</tt>相同。创建<tt>HashMap</tt>时,默认负载因子(0.75)和初始容量足以容纳指定的<tt>Map</tt>中的映射。 * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ publicHashMap(Map<? extends K, ? extends V> m){ this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); }
/** * Returns a power of two size for the given target capacity. * 返回给定目标容量的2次幂大小。这是一个算法,还是比较抽象的,可以不用理解那么深。 * 必须要保证容量总是2的幂。这样寻找数据的算法才有效。 */ staticfinalinttableSizeFor(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; }
/** * Implements Map.putAll and Map constructor. * 实现了putAll和Map构造函数。 * * @param m the map * @param evict false when initially constructing this map, else * true (relayed to method afterNodeInsertion). */ finalvoidputMapEntries(Map<? extends K, ? extends V> m, boolean evict){ int s = m.size(); //首先判断输入进来的map是否有容量,得有容量才能进行操作 if (s > 0) { //判断该HashMap是否已经初始化过了 if (table == null) { // pre-size float ft = ((float)s / loadFactor) + 1.0F; //判断ft是否大于最大值,取得这个map的容量 int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY); if (t > threshold) //调用上面的方法,返回给定目标容量的2次幂大小 threshold = tableSizeFor(t); } //已初始化,并且m的size大于临界值就需要扩容 elseif (s > threshold) resize(); //循环遍历添加到HashMap中 for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { K key = e.getKey(); V value = e.getValue(); putVal(hash(key), key, value, false, evict); } } }
/** * 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. * *将map中的指定值与指定键关联。如果之前的映射包含键的映射,则旧值将被替换。 * @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){ return putVal(hash(key), key, value, false, true); }
/** * Implements Map.put and related methods. * 这个翻译没啥意思,就是实现了put方法 * @param hash hash for key 键地哈希值 * @param key the key 键 * @param value the value to put 值 * @param onlyIfAbsent if true, don't change existing value onlyIfAbsent如果为true,则不更改现有值,可能这个有点难懂,看下面方法体里面的代码就知道了 * @param evict if false, the table is in creation mode. 如果为false,则表示表处于创建模式。可能这个有点难懂,看下面方法体里面的代码就知道了 * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict){ //初始化1个空的节点数组(用于承接本类中的桶),初始化1个空节点(用于承接该key位置的节点),两个int值(承接容量) Node<K,V>[] tab; Node<K,V> p; int n, i; //如果桶是空的,也就意味着第一次插入数据 if ((tab = table) == null || (n = tab.length) == 0) //如果是第一次插入,则进行第一次扩容 n = (tab = resize()).length; //(n - 1) & hash是确定元素放入哪个桶中,如果桶是空的,则新生成节点放进去就行了 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { //如果桶里面有数据 //定义一个节点用于插入数据 Node<K,V> e; K k; //比较桶里面的第一个元素,如果刚好是这个key,则直接替换 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //判断是否是树节点,如果是就放入树中 elseif (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); //如果插入以后结点数量达到阈值(8),则触发方法 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; //用于遍历链表,与前面的e = p.next组合,可以遍历链表 p = e; } } //如果e不是空,说明已经找到了与之匹配的节点 if (e != null) { // existing mapping for key V oldValue = e.value; //这里根据方法最上面的配置来决定是否替换数据 if (!onlyIfAbsent || oldValue == null) e.value = value; //该方法在HashMap中是空方法 afterNodeAccess(e); return oldValue; } } ++modCount; //判断是否需要扩容 if (++size > threshold) resize(); //该方法在HashMap中是空方法 afterNodeInsertion(evict); returnnull; }
/** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * 返回指定键映射到的值,如果此映射不包含键的映射,则返回 {@code null}。 * * <p>More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise * it returns {@code null}. (There can be at most one such mapping.) * 更正式地说,如果此映射包含从键 k 到值 v 的映射,使得 {(key==null ? k==null : key.equals(k ))},则此方法返回 {@code v}; 否则返回 {@code null}。 (最多可以有一个这样的映射。) * * <p>A return value of {@code null} does not <i>necessarily</i> * indicate that the map contains no mapping for the key; it's also * possible that the map explicitly maps the key to {@code null}. * The {@link #containsKey containsKey} operation may be used to * distinguish these two cases. * * {@code null} 的返回值并不<i>必然</i>表示映射不包含键的映射; 地图也有可能将键显式映射到 {@code null}。 {@link #containsKey containsKey} 操作可用于区分这两种情况。 * @see #put(Object, Object) * 说了这么多是什么意思呢?其实就是get方法返回null的情况有两种,一种是根本不存在这个键,另一种情况是这个键本身存储的数据就是null。 */ public V get(Object key){ Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; }
/** * Implements Map.get and related methods. * * @param hash hash for key * @param key the key * @return the node, or null if none */ final Node<K,V> getNode(int hash, Object key){ //定义一个node的数组用于临时保存桶,定义一个节点用来指向桶中的首个节点,定义int来代表桶的长度,定义一个K来定义键 Node<K,V>[] tab; Node<K,V> first, e; int n; K k; //如果桶不是空的,且桶中有数据(也就是桶中有第一个节点不为空) if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //总是先检查第一个节点,判断键和hash是否相同,如果相同则返回 if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; //如果不同的话就遍历这个链表找到相同的 if ((e = first.next) != null) { //如果属于树节点,就使用树的遍历方式 if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } returnnull; }