HashMap的抽丝剥茧

Win10文件夹只读属性自动恢复?彻底解决方法解析 Win10文件夹只读属性自动恢复?彻底解决方法解析在Windows系统使用过程中,文件权限管理常会遇到棘手问题。最近多位用户反馈:取消文件夹只读属性后,系统却自动恢复该设置,导致无法正常复制或修改文件。本文将深入分析该现象的产生原因,并提供多种解决方案,帮助开发者彻底摆脱权限困扰。 阅读详情

前言:
对于HashMap的一些重点方法进行了注释。还有大多数没有进行注释的部分就尽量不弄进来了,以免影响观看。

package java.util;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 插入、获取的时间复杂度基本是 O(1)(前提是有适当的哈希函数,让元素分布在均匀的位置)
 * 还有关于红黑树的操作后续再看并总结吧
 * 
 * 再看完构造方法和put方法后可以发现
 * 事实上,new HashMap();完成后,如果没有put操作,是不会分配存储空间的。
 * 
 * 添加操作:
 * 1.当桶数组 table 为空时,通过扩容的方式初始化 table
 * 2.查找要插入的键值对是否已经存在,存在的话根据条件判断是否用新值替换旧值
 * 3.如果不存在,则将键值对链入链表中,并根据链表长度决定是否将链表转为红黑树
 * 4.判断键值对数量是否大于阈值,大于的话则进行扩容操作
 * 
 * 还说下注意点:
 * 1.HashMap有个MIN_TREEIFY_CAPACITY代表:桶中结构转化为红黑树对应的table的最小大小。
 * 当需要将解决 hash 冲突的链表转变为红黑树时,需要判断下此时数组容量,若是由于数组容量太小(小于 MIN_TREEIFY_CAPACITY )
 * 导致的 hash 冲突太多,则不进行链表转变为红黑树操作,转为利用 resize() 函数对 hashMap 扩容。
 * 所以并不是桶子上有8位元素的时候它就能变成红黑树,它得同时满足我们的散列表容量大于64才行的
 * 
 * 2.请问HashMap在什么时候扩容?
 * 一定是当size达到总容量的0.75时会扩容吗?这个不一定,得看jdk的版本,1.8以上put操作时确实对是否扩容只有loadFactor这个因素
 * 在1.7的源码中的put操作时扩容的条件为“(size >= threshold) && (null != table[bucketIndex])”,也就是说还需要同时满足后面条件,
 * 那么bucketIndex又是什么呢?直译为“桶的下标”,即下一个存放Entry的桶的位置。简而言之,
 * 仅当size >= threshold且发生Hash值%(length-1)冲突(或修改已存在的值或)时,才会进行扩容。
 * 
 * 3.还有关于1.8和1.7的一些改动:
 * 数据结构:
 * JDK1.7使用数组+链表的数据结构,而1.8使用数组+链表+红黑树。
 * 如果插入key的hashcode相同,使用链表方式解决冲突,当链表长度达到8个(默认设置的阈值)时,
 * 调用treeifyBin函数,将链表转换为红黑树。红黑树的时间复杂度为O(log n),即put/get最坏时间复杂度为O(log n)。而使用链表的话,则是O(n)
 * 数据存储机制:
 * 发生hash冲突时,JDK1.7采用链地址法+头插法,而1.8采用链地址法+尾插法+红黑树。
 * 头插入法插入效率较高,但容易出现逆序且环形链表死循环问题,尾插法可避免此问题。
 * 
 * 4.为什么要用红黑树,而不用平衡二叉树?
 * 插入效率比平衡二叉树高,查询效率比普通二叉树高。所以选择性能相对折中的红黑树
 * 
 * 5. JDK1.7是基于数组+单链表实现(为什么不用双链表)
 * 首先,用链表是为了解决hash冲突。单链表能实现为什么要用双链表呢?(双链表需要更大的存储空间)
 * 
 * 6.再谈下和Hashtable的区别及多线程情况下使用什么:
 * 从存储结构和实现来讲基本上都是相同的。它和HashMap的最大的不同是它是线程安全的,另外它不允许key和value为null。
 * Hashtable是个过时的集合类,不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换
 * 还有一种就是 Map m = Collections.synchronizedMap(new HashMap(...));
 * 
 * 7.重写对象的Equals方法时,要重写hashCode方法,为什么?跟HashMap有什么关系?
equals与hashcode间的关系:

    如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
    如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)

因为在 HashMap 的链表结构中遍历判断的时候,特定情况下重写的 equals 方法比较对象是否相等的业务逻辑比较复杂,循环下来更是影响查找效率。所以这里把 hashcode 的判断放在前面,只要 hashcode 不相等就玩儿完,不用再去调用复杂的 equals 了。很多程度地提升 HashMap 的使用效率。

所以重写 hashcode 方法是为了让我们能够正常使用 HashMap 等集合类,因为 HashMap 判断对象是否相等既要比较 hashcode 又要使用 equals 比较。而这样的实现是为了提高 HashMap 的效率。
 * 
 * 8. 既然红黑树那么好,为啥hashmap不直接采用红黑树,而是当大于8个的时候才转换红黑树?
 * 因为红黑树需要进行左旋,右旋操作, 而单链表不需要。
 * 以下都是单链表与红黑树结构对比。
 * 如果元素小于8个,查询成本高,新增成本低。
 * 如果元素大于8个,查询成本低,新增成本高。
 * 至于为什么选数字8,是大佬折中衡量的结果-.-,就像loadFactor默认值0.75一样。
 * 
 * 9.其他
 * 扩容后是原先容量的两倍
 * 
 * 底层数组的长度要求2的次方(即使不是2的次方也会经过tableSizeFor转为2的次方):
 * 首先,capacity 为 2的整数次幂的话,计算桶的位置 h&(length-1) 就相当于对 length 取模,提升了计算效率;
 * 其次,capacity 为 2 的整数次幂的话,为偶数,这样 capacity-1 为奇数,奇数的最后一位是 1,
 * 这样便保证了 h&(capacity-1) 的最后一位可能为 0,也可能为 1(这取决于h的值),即与后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性;
 * 而如果 capacity 为奇数的话,很明显 capacity-1 为偶数,它的最后一位是 0,这样 h&(capacity-1) 的最后一位肯定为 0,
 * 即只能为偶数,这样任何 hash 值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间。
 * 
 * 怎样通过key获得数组中的索引呢?i=(length - 1) & hash 类似于取余,但是效率高一些
 * 
 * 
 * 
 * 
 * 
 * 
 * Hash table based implementation of the <tt>Map</tt> interface.  This
 * implementation provides all of the optional map operations, and permits
 * <tt>null</tt> values and the <tt>null</tt> key.(这里说的允许key和value为空)   (The <tt>HashMap</tt>
 * class is roughly equivalent to <tt>Hashtable</tt>, except that it is
 * unsynchronized and permits nulls.)  This class makes no guarantees as to
 * the order of the map; in particular, it does not guarantee that the order
 * will remain constant over time.
 * 上面一段主要讲了允许key和value为null,且说了几乎等同于Hashtable除了不同步和允许为null外
 * 然后还说了此类不保证映射的顺序,特别是它不保证该顺序恒久不变。(个人理解是rehash时会导致顺序变化) 
 *
 * <p>This implementation provides constant-time performance for the basic
 * operations (<tt>get</tt> and <tt>put</tt>), assuming the hash function
 * disperses the elements properly among the buckets.  Iteration over
 * collection views requires time proportional to the "capacity" of the
 * <tt>HashMap</tt> instance (the number of buckets) plus its size (the number
 * of key-value mappings).  Thus, it's very important not to set the initial
 * capacity too high (or the load factor too low) if iteration performance is
 * important.
 * 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。
 * 迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。
 * 所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。 
 *
 * <p>An instance of <tt>HashMap</tt> has two parameters that affect its
 * performance: <i>initial capacity</i> and <i>load factor</i>.  The
 * <i>capacity</i> is the number of buckets in the hash table, and the initial
 * capacity is simply the capacity at the time the hash table is created.  The
 * <i>load factor</i> is a measure of how full the hash table is allowed to
 * get before its capacity is automatically increased.  When the number of
 * entries in the hash table exceeds the product of the load factor and the
 * current capacity, the hash table is <i>rehashed</i> (that is, internal data
 * structures are rebuilt) so that the hash table has approximately twice the
 * number of buckets.
 *HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶的数量,
 *初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。
 *当哈希表中的条目数超出了加载因子与当前容量的乘积时,
 *则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。 
 *
 * <p>As a general rule, the default load factor (.75) offers a good
 * tradeoff between time and space costs.  Higher values decrease the
 * space overhead but increase the lookup cost (reflected in most of
 * the operations of the <tt>HashMap</tt> class, including
 * <tt>get</tt> and <tt>put</tt>).  The expected number of entries in
 * the map and its load factor should be taken into account when
 * setting its initial capacity, so as to minimize the number of
 * rehash operations.  If the initial capacity is greater than the
 * maximum number of entries divided by the load factor, no rehash
 * operations will ever occur.
 * 默认的装载因子为0.75,过高的装载因子虽然会降低空间消耗,但是会增加查找的时间消耗
 * 在设置初始化参数时,应该考虑好装载因子和实体数目,以便最大限度地减少 rehash 操作次数。
 * 如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。 
 *
 * <p>If many mappings are to be stored in a <tt>HashMap</tt>
 * instance, creating it with a sufficiently large capacity will allow
 * the mappings to be stored more efficiently than letting it perform
 * automatic rehashing as needed to grow the table.  Note that using
 * many keys with the same {@code hashCode()} is a sure way to slow
 * down performance of any hash table. To ameliorate impact, when keys
 * are {@link Comparable}, this class may use comparison order among
 * keys to help break ties.
 * 如果许多映射要存储在HashMap中,那么创建一个足够大的容量将让映射被更有效地存储,而不是让它执行再hash。
 * 
 *
 * <p><strong>Note that this implementation is not synchronized.</strong>
 * If multiple threads access a hash map concurrently, and at least one of
 * the threads modifies the map structurally, it <i>must</i> be
 * synchronized externally.  (A structural modification is any operation
 * that adds or deletes one or more mappings; merely changing the value
 * associated with a key that an instance already contains is not a
 * structural modification.)  This is typically accomplished by
 * synchronizing on some object that naturally encapsulates the map.
 * 这里强调的是不同步
 *
 * If no such object exists, the map should be "wrapped" using the
 * {@link Collections#synchronizedMap Collections.synchronizedMap}
 * method.  This is best done at creation time, to prevent accidental
 * unsynchronized access to the map:<pre>
 *   Map m = Collections.synchronizedMap(new HashMap(...));</pre>
 *   这里强调的是怎样使其成为一个同步的容器
 *
 *
 * 下面的基本在介绍迭代器和fail-fast
 * <p>The iterators returned by all of this class's "collection view methods"
 * are <i>fail-fast</i>: if the map is structurally modified at any time after
 * the iterator is created, in any way except through the iterator's own
 * <tt>remove</tt> method, the iterator will throw a
 * {@link ConcurrentModificationException}.  Thus, in the face of concurrent
 * modification, the iterator fails quickly and cleanly, rather than risking
 * arbitrary, non-deterministic behavior at an undetermined time in the
 * future.
 *
 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
 * as it is, generally speaking, impossible to make any hard guarantees in the
 * presence of unsynchronized concurrent modification.  Fail-fast iterators
 * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
 * Therefore, it would be wrong to write a program that depended on this
 * exception for its correctness: <i>the fail-fast behavior of iterators
 * should be used only to detect bugs.</i>
 *
 * <p>This class is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @param <K> the type of keys maintained by this map
 * @param <V> the type of mapped values
 *
 * @author  Doug Lea
 * @author  Josh Bloch
 * @author  Arthur van Hoff
 * @author  Neal Gafter
 * @see     Object#hashCode()
 * @see     Collection
 * @see     Map
 * @see     TreeMap
 * @see     Hashtable
 * @since   1.2
 */
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    private static final long serialVersionUID = 362498820763181265L;


    /**
     * The default initial capacity - MUST be a power of two.、
     * 必须是 2 的整数次方
     * 默认初始化容量,<<代表向左四位,所以1变为16
     * 
     * 至于为什么是2的次方,后总结https://blog.youkuaiyun.com/oqqYeYi/article/details/39831029
     * 或者https://www.ibm.com/developerworks/cn/java/j-lo-hash/?open&cm_mmc=6505-_-n-_-vrm_newsletter-_-10104_142587&cmibm_em=dm:0:10631101
     * 
     * HashMap 底层数组的长度总是 2 的 n 次方,这一点可参看后面关于 HashMap 构造器的介绍。
     * 当 length 总是 2 的倍数时,h & (length-1)将是一个非常巧妙的设计:假设 h=5,length=16, 那么 h & length - 1 将得到 5;
     * 如果 h=6,length=16, 那么 h & length - 1 将得到 6 ……如果 h=15,length=16, 那么 h & length - 1 将得到 15;
     * 但是当 h=16 时 , length=16 时,那么 h & length - 1 将得到 0 了;当 h=17 时 , length=16 时,
     * 那么 h & length - 1 将得到 1 了……这样保证计算得到的索引值总是位于 table 数组的索引之内。
     */
    static final int 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的幂且小于2的30次方,传入容量过大将被这个值替换)
     * 
     * 这里相当于2的30次方,不用Math的方法进行运算是因为位运算效率高点
     * 还有明明int是4个字节即32位,不应该是2的31次方吗?
     * 原因在于第一位是用作符号位的。用来表示正负之分(0为正,1为负)
     * 
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * The load factor used when none specified in constructor.
     * 默认装载因子
     * 结合时间和空间效率考虑得到的一个折中方案
     */
    static final float 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.
     * 
     * 这个常量名的意思根据英文名看出是链表转为红黑树的门槛
     * 当同一个桶中超过8个元素即进行转换
     * 
     * 链表转成树的阈值,当桶中链表长度大于8时转成树 
     * threshold = capacity * loadFactor
     * 
     */
    static final int 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.
     * 
     * 这个和上一个常量意思相反,即树转为list的界限。
     * 进行resize操作时,当桶中数量小于6转为链表
     */
    static final int 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.
     * 
     * 最小树化容量(桶的数目即数组的长度)
     * 
     * 
     *  桶中结构转化为红黑树对应的table的最小大小
     *  当需要将解决 hash 冲突的链表转变为红黑树时,
     *  需要判断下此时数组容量,
     *  若是由于数组容量太小(小于 MIN_TREEIFY_CAPACITY )
     *  导致的 hash 冲突太多,则不进行链表转变为红黑树操作,
     *  转为利用 resize() 函数对 hashMap 扩容
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

    /**
     * Basic hash bin node, used for most entries.  (See below for
     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
     */
    static class Node<K,V> implements Map.Entry<K,V> {
    	// hash值代表位置
        final int hash;
        final K key;
        V value;
        // 下一个节点的指针
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
        	// 这个是按位异或
        	// Objects中的hashCode方法会进行null判断,为null时hashCode直接为0 不为null才去执行Object中的本地hashCode方法
        	// 由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。
        	// (这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 Java 编程语言不需要这种实现技巧。)
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        /**
         * 设置成新值返回旧值
         */
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
        	// 先判断引用是否一致。不一致再看key和value的hashCode值
            if (o == this)
                return true;
            // 先判断类型
            if (o instanceof Map.Entry) {
            	// 这里不知道为什么还要使用一个临时变量
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                
                // 必须key和value的hashCode值同时相同才返回true
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

    /* ---------------- Static utilities -------------- */

   
    static final int hash(Object key) {
        int h;
        // h >>> 16是用来取出h的高16 由于int只有32位,无符号向右移16位就代表取最先的16位即高16位
        // 如0000 0100 1011 0011  1101 1111 1110 0001 -> 0000 0000 0000 0000  0000 0100 1011 0011
        // 那么为什么(h = key.hashCode())要与(h >>> 16)进行亦或呢?
        // 将键的hashcode的高16位异或低16位(高位运算),这样即使数组table的length比较小的时候,也能保证高低Bit都参与到Hash的计算中,同时不会有太大的开销;
        // 另一个解释(为什么要先高16位异或低16位再取余运算):
        // 当我们的length为16(即长度比较小)的时候,哈希码(字符串“abcabcabcabcabc”的key对应的哈希码)对(16-1)&操作,对于多个key生成的hashCode,只要哈希码的后4位为0,
        // 不论不论高位怎么变化,最终的结果均为0。也就是说,如果只取后四位(低位)的话,这个时候产生"碰撞"的几率就非常大(当然&运算中产生碰撞的原因很多,
        // 这里只是举个例子)。为了解决低位与操作碰撞的问题,于是便有了第二步中高16位异或低16位的“扰动函数”。
        // 右移16位,自己的高半区和低半区异或,就是为了混合原始哈希码的高位和低位,以此来加大低位随机性。减少了碰撞冲突的可能性!
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    /**
     * Returns x's Class if it is of the form "class C implements
     * Comparable<C>", else null.
     */
    static Class<?> comparableClassFor(Object x) {
        if (x instanceof Comparable) {
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType)t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                        return c;
                }
            }
        }
        return null;
    }

    /**
     * Returns k.compareTo(x) if x matches kc (k's screened comparable
     * class), else 0.
     */
    @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
    static int compareComparables(Class<?> kc, Object k, Object x) {
        return (x == null || x.getClass() != kc ? 0 :
                ((Comparable)k).compareTo(x));
    }

    /**
     * Returns a power of two size for the given target capacity.
     * 根据指定的容量计算出合适的阈值
     * 经过若干次无符号右移、求异运算,得出最接近指定参数 cap 的 2 的 N 次方容量。假如你传入的是 5,返回的初始容量为 8 。
     * 刚刚自己测试的结果是返回大于等于当前值的2的N次方
     */
    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;
    }

    /* ---------------- Fields -------------- */

    /**
     * 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.)
     * 
     * 哈希表中的链表数组,代表桶的集合,每一个桶又是一个链表
     */
    transient Node<K,V>[] table;

    /**
     * Holds cached entrySet(). Note that AbstractMap fields are used
     * for keySet() and values().
     * 缓存的 键值对集合(另外两个视图:keySet 和 values 是在 AbstractMap 中声明的)
     */
    transient Set<Map.Entry<K,V>> entrySet;

    /**
     * The number of key-value mappings contained in this map.
     * 键值对的数量(不是数组中包含得键值对,而是指得所有得)
     */
    transient int 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 修改的次数,这个变量用来保证 fail-fast 机制
     */
    transient int modCount;

    /**
     * The next size value at which to resize (capacity * load factor).
     *
     * 阈值 下次需要扩容时的值,等于 容量*加载因子
     * threshold = 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
     */
    final float loadFactor;

    /* ---------------- Public operations -------------- */

    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and load factor.
     * 
     * 构建一个带有指定初始化容量和加载因子的空的HashMap
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public HashMap(int initialCapacity, float loadFactor) {
    	// 初始化容量小于0会抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        
        // 如果传的容量值太过高大于MAXIMUM_CAPACITY,则令初始化参数为最大值
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        // 如果加载因子小于等于0或是加载因子为nan(Not-a-Number)这种就会抛出异常
        // JDK中float和double有一个方法isNan,该方法用于描述非法的float,经过多次运算float值可能会出现非法情况,如除数为0.0
        // 在Float中NaN实际上是引用类型,而不是值类型,每一个NaN都是不同的对象(即nan!=nan)。
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        // 如果没有异常则赋值
        this.loadFactor = loadFactor;
        // 根据初始化容量设置一个合适的阈值
        this.threshold = tableSizeFor(initialCapacity);
        
        // 不过这里好像没有成员变量Capacity还有疑问的是threshold不应该是initialCapacity*loadFactor吗
        // 在1.7的源码是有一个局部变量capacity的,也是用来计算threshold并初始化table
        /*
         *  // 计算出大于 initialCapacity 的最小的 2 的 n 次方值。
    int capacity = 1; 
    // 这个就类似于jdk1.8的tableSizeFor方法
    while (capacity < initialCapacity) 
        capacity <<= 1; 
    this.loadFactor = loadFactor; 
    // 设置容量极限等于容量 * 负载因子
    threshold = (int)(capacity * loadFactor); 
    // 初始化 table 数组
    table = new Entry[capacity];           
    init(); 
         */
        
        // 然后关于1.8中为啥是将2的整数幂的数赋给threshold?
        // threshold这个成员变量是阈值,决定了是否要将散列表再散列。它的值应该是:capacity * load factor才对的。
        // 原因:其实这里仅仅是一个初始化,当创建哈希表的时候,它会重新赋值的即第一次的resize()中
        
        
        
    }

    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and the default load factor (0.75).
     * 
     * 构建一个带有指定初始化容量和默认加载因子0.75的空的HashMap
     *
     * @param  initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    public HashMap(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).
     * 构建一个带有指定默认容量16和默认加载因子0.75的空的HashMap
     */
    public HashMap() {
        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>.
     * 
     * 创建一个内容为参数 m 的内容的哈希表
     *
     * @param   m the map whose mappings are to be placed in this map
     * @throws  NullPointerException if the specified map is null
     */
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }

    /**
     * Implements Map.putAll and Map constructor
     *
     * @param m the map
     * @param evict false when initially constructing this map, else
     * true (relayed to method afterNodeInsertion).
     * evict当初始化map时为false,否则为true
     */
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
        	//数组还是空,初始化参数
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            //数组不为空,超过阈值就扩容
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                //先经过 hash() 计算位置,然后复制指定 map 的内容
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

    /**
     * Returns the number of key-value mappings in this map.
     *
     * @return the number of key-value mappings in this map
     */
    public int size() {
        return size;
    }

    /**
     * Returns <tt>true</tt> if this map contains no key-value mappings.
     *
     * @return <tt>true</tt> if this map contains no key-value mappings
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <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.)
     *
     * <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.
     *
     * @see #put(Object, Object)
     */
    public V get(Object key) {
        Node<K,V> e;
        // 根据hash(key)计算出位置,再调用getNode找到节点
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

    /**
     * Implements Map.get and related methods
     * 
     * 实现map的get及其相关方法
     *
     * @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<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) {
        	// first是桶中的元素,如果只有这一个节点(不是链表)就可以直接返回了
        	// 或者是链表但是首节点与传来的key一致(同一条链表上的节点主要是key的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 { // 链表 遍历找到equal相等的,先使用hash判断排除大部分相比于直接使用eauals判断提高一些效率
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

    /**
     * Returns <tt>true</tt> if this map contains a mapping for the
     * specified key.
     * 相当于使用get方法,看是否取得到
     * @param   key   The key whose presence in this map is to be tested
     * @return <tt>true</tt> if this map contains a mapping for the specified
     * key.
     */
    public boolean containsKey(Object key) {
        return getNode(hash(key), key) != null;
    }

    /**
     * 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) {
        return putVal(hash(key), key, value, false, true);
    }

    /**
     * Implements Map.put and related methods
     * 实现map的put和相关的方法
     * 
     * 将值存入map的实际方法
     *
     * @param hash hash for key // 存储的位置
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value // 如果为true,则不改变存在的值
     * @param evict if false, the table is in creation mode. // 这个之前说过,当为true代表是在用另一个map进行初始化(最后一种构造方法)
     * 除了这种情况,其他时候evict都为false
     * @return previous value, or null if none 如果之前存在返回之前的值,不存在则返回null
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        
        // 当散列表为空时,调用resize初始化散列表
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

        // 在jdk1.7中有indexFor(int h, int length)方法。jdk1.8里没有,但原理没变。]
        // 1.8中用tab[(n - 1) & hash]代替但原理一样。
        
        // “模”运算的消耗还是比较大的所以使用h & (length-1) 其实就是取余
        // 这个方法非常巧妙,它通过 h & (table.length -1) 来得到该对象的保存位,而HashMap底层数组的长度总是 2 的 n 次方,这是HashMap在速度上的优化
        // 当length总是 2 的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。
        // 取模(取余)运算转化成位运算公式:a%(2^n) 等价于 a&(2^n-1),而&操作比%操作具有更高的效率。
        
        // 如果要插入的位置没有元素,新建个节点并放进去即没有发生碰撞时,直接添加元素到散列表中
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else { // 发生了碰撞
            Node<K,V> e; K k;
            
            // 如果要插入的元素,桶的key和hash都相等(即与首节点匹配),记录下来
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            // 如果是红黑树结构,则调用树的插入方法
            else if (p instanceof TreeNode)
            	// 放入树中
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else { 
            	// 链表结构,找到了key映射的节点,就记录这个节点,退出循环
            	// 如果没有找到,在链表尾部插入节点。插入后如果发现临界值大于TREEIFY_THRESHOLD
            	// ,转为红黑树
            	
            	// 对链表进行遍历,并统计链表长度
                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表示是否仅在lodValue为null情况下更新键值对的值
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

    /**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     * 初始化时需要调用,当散列表元素大于阈值threshold时也要调用
     *
     * @return the table
     */
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        // 这里就代表不是第一次扩容
        if (oldCap > 0) {
        	// 如果旧的容量比最大容量还要大,那不能散列,返回旧的散列表
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            // 新的阈值是旧的两倍 double threshold 且容量也为旧的两倍
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        // 如果旧容量<=0 而旧阈值>0,数组的新容量设置为老数组扩容的阈值 
        // 这个应该使用的是带参数的构造函数(这个已经将阈值弄为2的次方了(使用tableSizeFor方法),所以不用担心容量不为2的次方了)
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        
        // 这里是旧容量<=0 且旧阈值<=0 旧阈值<=0,代表使用的默认不带参数的构造函数
        // 第一次初始化散列表的操作到oldTab != null之前
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor; // 这里就是对使用的带参数的构造函数的后续处理
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        // 这里回答了为什么阈值之前不是容量和加载因子的乘积了,在resize初始化table时会重新赋值
        threshold = newThr;
        @SuppressWarnings({"unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        
        // 将旧散列表复制到新散列表中
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode) // 是红黑树
                    	// 重新映射时,需要对红黑树进行拆分
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order 链表
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        // 遍历链表,并将链表节点按原顺序进行分组
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        // 将分组后的链表映射到新桶中
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

    /**
     * Replaces all linked nodes in bin at index for given hash unless
     * table is too small, in which case resizes instead.
     * 将链表转为
     * 
     * 
     */
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }

    /**
     * Copies all of the mappings from the specified map to this map.
     * These mappings will replace any mappings that this map had for
     * any of the keys currently in the specified map.
     *
     * @param m mappings to be stored in this map
     * @throws NullPointerException if the specified map is null
     */
    public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }

    /**
     * Removes the mapping for the specified key from this map if present.
     * 根据键值删除键值对,如果哈希表中存在该键,那么返回键对应的值,否则返回null
     * @param  key key whose mapping is to be removed from the map
     * @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 remove(Object key) {
        Node<K,V> e;
        // 同样也是找到先用key算出hash再在removeNode中使用(n - 1) & hash进行其他运算
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }

    /**
     * Implements Map.remove and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to match if matchValue, else ignored 值如果不匹配就忽略
     * @param matchValue if true only remove if value is equal 如果值相等是这个为true
     * @param movable if false do not move other nodes while removing
     * @return the node, or null if none
     */
    final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        // 桶不为空,映射的hash值也存在
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            Node<K,V> node = null, e; K k; V v;
            // 在桶的首位就找到了对应的元素了,记录下来
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            // 不是在首位,就得去红黑树或者链表中查找了
            else if ((e = p.next) != null) {
                if (p instanceof TreeNode) // 头节点不匹配,且头节点为TreeNode
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {  // 头节点不匹配,且头节点为Node
                	// 遍历链表,一旦匹配,跳出循环
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            // 找到了对应的节点,并且要么没传value要么value也能匹配,那么就分三种情况去删除了
            // 1.链表 2.红黑树 3.在桶的首位
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
            	// 如果待删除节点是TreeNode,使用红黑树的方法
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                else if (node == p) // 如果待删除节点是头节点,更改桶中的头节点即可
                    tab[index] = node.next;
                // 在链表遍历过程中,p代表node节点的前驱节点
                else
                    p.next = node.next;
                ++modCount;
                --size;
                // 子类实现
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }

    /**
     * Removes all of the mappings from this map.
     * The map will be empty after this call returns.
     */
    public void clear() {
        Node<K,V>[] tab;
        modCount++;
        if ((tab = table) != null && size > 0) {
            size = 0;
            for (int i = 0; i < tab.length; ++i)
                tab[i] = null;
        }
    }

    /**
     * Returns <tt>true</tt> if this map maps one or more keys to the
     * specified value.
     *
     * @param value value whose presence in this map is to be tested
     * @return <tt>true</tt> if this map maps one or more keys to the
     *         specified value
     */
    public boolean containsValue(Object value) {
        Node<K,V>[] tab; V v;
        if ((tab = table) != null && size > 0) {
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                    if ((v = e.value) == value ||
                        (value != null && value.equals(v)))
                        return true;
                }
            }
        }
        return false;
    }

还有处于比较后面的红黑树的一些代码(中间一部分没了解):

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }

        /**
         * Returns root of tree containing this node.
         * 根据当前节点找到根节点
         */
        final TreeNode<K,V> root() {
        	// 遍历,直到parent为null为止
            for (TreeNode<K,V> r = this, p;;) {
                if ((p = r.parent) == null)
                    return r;
                r = p;
            }
        }
         /**
         * Finds the node starting at root p with the given hash and key.
         * The kc argument caches comparableClassFor(key) upon first use
         * comparing keys.
         * 
         * 从根节点开始,根据指定的key查找节点
         * 至于kc这个参数暂时也没弄清楚是干啥的,后面看到其他地方使用再理解吧,默认使用null即可
         */
        final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
            TreeNode<K,V> p = this;
            do {
                int ph, dir; K pk;
                TreeNode<K,V> pl = p.left, pr = p.right, q;
                // 根据大小判断在此节点还是在此节点的左右枝
                if ((ph = p.hash) > h) // 左枝
                    p = pl;
                else if (ph < h) // 右枝
                    p = pr;
                else if ((pk = p.key) == k || (k != null && k.equals(pk))) // 当前节点
                    return p;
                else if (pl == null)
                    p = pr;
                else if (pr == null)
                    p = pl;
                else if ((kc != null ||
                          (kc = comparableClassFor(k)) != null) &&
                         (dir = compareComparables(kc, k, pk)) != 0)
                    p = (dir < 0) ? pl : pr;
                else if ((q = pr.find(h, k, kc)) != null)
                    return q;
                else
                    p = pl;
            } while (p != null);
            return null;
        }

        /**
         * Calls find for root node.
         * 从根节点开始查找
         * 先判断当前节点的parent是否为null即判断当前节点是否为根节点。
         * 如果不为根节点再调用root()方法找到根节点
         * 再调用根节点的find方法根据指定的key查找键值对
         */
        final TreeNode<K,V> getTreeNode(int h, Object k) {
            return ((parent != null) ? root() : this).find(h, k, null);
        }
         /**
         * Splits nodes in a tree bin into lower and upper tree bins,
         * or untreeifies if now too small. Called only from resize;
         * see above discussion about split bits and indices.
         * 
         * 将红黑树转为链表
         *
         * @param map the map
         * @param tab the table for recording bin heads
         * @param index the index of the table being split
         * @param bit the bit of hash to split on
         */
        final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
            TreeNode<K,V> b = this;
            // Relink into lo and hi lists, preserving order
            TreeNode<K,V> loHead = null, loTail = null;
            TreeNode<K,V> hiHead = null, hiTail = null;
            int lc = 0, hc = 0;
            for (TreeNode<K,V> e = b, next; e != null; e = next) {
                next = (TreeNode<K,V>)e.next;
                e.next = null;
                if ((e.hash & bit) == 0) {
                    if ((e.prev = loTail) == null)
                        loHead = e;
                    else
                        loTail.next = e;
                    loTail = e;
                    ++lc;
                }
                else {
                    if ((e.prev = hiTail) == null)
                        hiHead = e;
                    else
                        hiTail.next = e;
                    hiTail = e;
                    ++hc;
                }
            }

            if (loHead != null) {
                if (lc <= UNTREEIFY_THRESHOLD)
                    tab[index] = loHead.untreeify(map);
                else {
                    tab[index] = loHead;
                    if (hiHead != null) // (else is already treeified)
                        loHead.treeify(tab);
                }
            }
            if (hiHead != null) {
                if (hc <= UNTREEIFY_THRESHOLD)
                    tab[index + bit] = hiHead.untreeify(map);
                else {
                    tab[index + bit] = hiHead;
                    if (loHead != null)
                        hiHead.treeify(tab);
                }
            }
        }

Thanks

参考:

https://blog.youkuaiyun.com/qq_19431333/article/details/55505675
https://www.jianshu.com/p/2e2a18d02218
https://blog.youkuaiyun.com/u011240877/article/details/53351188?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1
https://blog.youkuaiyun.com/u011240877/article/details/53358305#hashmap-%E5%9C%A8-jdk-18-%E4%B8%AD%E6%96%B0%E5%A2%9E%E7%9A%84%E6%93%8D%E4%BD%9C-%E7%BA%A2%E9%BB%91%E6%A0%91%E4%B8%AD%E6%9F%A5%E6%89%BE%E5%85%83%E7%B4%A0-gettreenode
基础的pdf病毒木马 基础的pdf病毒木马 环境: 攻击机:Kali 目标操作系统:Windows 10或Windows 7 (关闭防火墙和安全软件) 目标软件:Adobe Reader 8.1.2 msf框架搜索:adobe-pdf 1.打开msfconsole 2.搜索 adobe_pdf模块 3.加载攻击模块并设置payload 4.运行生成pdf 5.将pdf拖入到win10 6.打开msf,加... 阅读详情

相关推荐

解决 Clawdbot 对接 Minimax 出现 401 (Token is unusable) 的问题排查与修复

即便确认 API Key 有效且余额充足,该问题仍可能在 Clawdbot 中持续出现,而在其他工具(如 curl 或 Postman)中测试正常。

hampeter的博客 3075

JDK8:HashMap源码解析:TreeNode类的treeify方法

一、概述 treeify方法是TreeNode类的一个实例方法,通过TreeNode对象调用,实现该对象打头的链表转换为树结构。   二、方法解析 /** * 参数为HashMap的元素数组 */ final void treeify(Node&lt;K,V&gt;[] tab) { TreeNode&lt;K,V&gt; root = null; // 定义树的根节点 ...

老艮头的后知后觉 6147

计算机视觉-深度学习图像检测方法梳理

计算机视觉-深度学习图像检测方法梳理 由于之后要转方向啦,趁这段时间整理手中硕士研究方向的一些阅读笔记,这是一篇关于计算机视觉的基础知识梳理 先搞清一些小知识点 首先我们要弄清楚图像分类、目标定位、语义分割、实例分割的区别 a. 图像分类 :给定一张输入图像,图像分类任务旨在判断该图像所属类别; b. 目标定位 :在图像分类的基础上,我们还想知道图像中的目标具体在图像的什么位置,通常是以包围盒的(bounding box)形式; c. 语义分割 :语义分割是目标检测更进阶的任务,目标检测只需要框出每个目

qq_41662991的博客 9953

hashmap源码分析1

之前看过hashmap源码,感觉最近有些遗忘,写一波复习一遍,首先查看hashmap的继承体系,可以发现hashMap实现了map,cloneble,serializable接口这些接口应该都很熟悉了吧,其次hashmap继承自abstractmap查看这个父类,发现他是一个抽象类,因此其中有些方法是可以复写的,具体方法查看源码可以发现包括entrySet其他方法可以看做map这种数据结构都通用的

qq_38801925的博客 253

(数字ic验证)从零开始的apb_watchdog验证模块搭建(一、功能介绍与环境搭建)

apb_watchdog验证模块搭建(一)文章目录 文章目录apb_watchdog验证模块搭建(一)文章目录前言一、apb_watchdog简单功能介绍二、测试平台环境搭建1.测试所用模板介绍2.tb与interface2.env与watchdog_pkg3.test与base_sequence4.Makefile总结——当前验证结构 前言 本文介绍了apb_watchdog验证模块的搭建过程与其具体代码,主要包括其中验证环境的构建,测试序列的发送与回归测试收集。主要运用systemvorilog与u

peop1e的博客 9737

Matlab/simulink频率响应分析方法

Matlab/Simulink仿真系列问题一.电气模块与信号模块 一.电气模块与信号模块

virtuoso~晓晓 3万+

HashMap

看源码doc注释比所有的博客、视频讲解都要权威! 所有翻译都是个人理解,仅供参考! Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null k...

miaokezhang的博客 249

高通8155源码下载与Android源码编译

一.下载代码 高通下载源码位置: Qualcomm CreatePoint 注册账号,然后登入 可以先选择edit -> infotainment -> sa8155 -> favorites, 点击View TOC就可以过滤出sa8155所有的信息目录.如下图 拉取代码链接在ChipCode中,点击进去可以看到所有的基线版本,选择你需要下载的版本点进去,如sa8155p-la-1-0_ap_standard_oem,把高通专有代码和开源代码都下载到本机. 1..

Johnny2004的专栏 7675

HashMap源码解析

继承体系 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。 HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。 HashMap 的实例有两个...

勿忘初心,方得始终 524

Matlab R2018b简体中文版完整安装图文教程(附安装包下载)

目 录 一、安装过程 二、下载地址 matlab R2018b安装包如下所示,包括主程序和补丁文件两部分。 一、安装过程 双击安装包中的setup.exe。 选择使用文件安装MIYAO,点击下一步。 接受协议,选择是,点击下一步。 输入序列号:09806-07443-53955-64350-21751-41297,点击下一步。 修改安装路径,点击下...

智慧空间实验室 1万+

大厂面经:大疆嵌入式面试题及参考答案(4万字长文:持续更新)

目录 Linux 系统调用的过程,中间发生了什么? 表格总结 Linux 中断流程,谈谈你对中断上下文的理解 中断流程 中断上下文理解 Linuxschedule()函数的原理和调用的时机 schedule()函数原理 调用时机 页表实现机制,分页的缺点? 页表机制 分页的缺点 介绍操作系统的多级反馈调度策略,时间片轮转,在项目中如何指定优先级来调度进程完成快速响应(nice命令) 多级反馈队列调度 时间片轮转 使用nice命令 聊内存分配,进程内存分配,段页式存

大模型大数据攻城狮的专栏 4169

HashMap源码浅析

HashMap简介 HashMap继承了AbstractMap,实现了Map, Cloneable, Serializable接口: HashMap的一些参数 HashMap的默认起始大小为16,最大容量为2^30: /** * The default initial capacity - MUST be a power of two. */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // ak

void 207

提示工程ROI计算模板:架构师私藏Excel表(含自动计算)

花3个人力优化提示词,到底能带来多少实际收益?“这个提示工程项目值得做吗?投入10万成本,多久能回本?“不同LLM应用的提示词优化优先级怎么排?全凭经验拍脑袋?当前90%的企业在提示工程投入上缺乏标准化的量化评估方法,导致资源浪费(盲目投入无回报项目)或错失机会(低估高价值优化点)。ROI = (总收益 - 总成本) / 总成本 × 100%对于提示工程,总成本=显性成本+隐性成本,总收益=直接收益+间接收益(后文会详细拆解)。提示工程的价值不是“玄学”,而是可以用数据量化的投资行为。本文分享的。

AI开发架构师 1073

抽丝剥茧解析 HashMap 源码【看完吊打面试官】

什么是HashMap? 简单来讲,HashMap是一种常用的数据结构,由数组+链表组成+红黑树(JDK1.8之后加入的),称为“哈希表”。 有的文章里说是把所有Node都放到数组里,这样描述可能不好理解,可以换一个说法,Node数组中只存放链表的第一个Node节点,由于这个Node节点会有指向下一个Node的next,就相当于是把链表放到了数组中。 HashMap的组成部分: 一、 6个默认值: 1.默认初始容量 static final int DEFAULT_INITIAL_CAPACITY = 1

MC_ADu的博客 783

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

HashMap真面目

AbstractMap抽象类实现了Map接口,具体化了部分Map接口定义的方法。实现了一个叫SimpleEntry的Entry(就是Map接口中定义的内部接口),还有一个叫SimpleImmutableEntry的Entry。暂且不表,不影响主题:识别HashMap真面目。

weixin_44612246的博客 1274

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

HashMap底层原理的故事

每个点都不是那么都容易。比如阅读源码。而且仅仅只是HashMap的源码。本篇博文的版本是基于java8的。 1.首先快乐的建立了一下测试代码。 public class HashMapTest { public static void main(String[] args) { HashMap<String, Object> hashMap = new HashMap<>(); hashMap.put(null, null); }

人称帅哥的专栏 431

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Java HashMap源码逐行浅析

相关的文章有很多,一如既往的,我认为不够详细,感觉不踏实。于是,抛开一切已知理论,重新进入源码抽丝剥茧。现将总结写出来,步骤详细,配图明晰,如有不足,欢迎指出。 源码版本是jdk 1.8 基本概念 数据结构 先用图说明HashMap的大概结构: HashMap由一个名为tab...

chennaoxi9356的博客 202

GO string 转map_抽丝剥茧—Go哈希Map的鬼魅神功

作者:Jonson51CTO学院 Golang与区块链高级讲师, kvb集团高级技术专家。github theWayToGolang作者,多年教育与金融行业从业经验。拥有丰富的高并发、分布式、微服务集群的实战经验。Go语言中的哈希Map是江湖上极厉害的一门武功,其入门简单,即便是掌握到了2、3层也具有四两拨千斤的神奇功效.因此成为江湖人士竞相研习的技艺,风头一时无两.但即便是成名已久...

weixin_39633089的博客 318

【总】JDK1.8源码 由浅入深!抽丝剥茧!重要类的归纳总结!

一、 java.lang 我的jdk源码(一):Object 一切类的根本! 我的jdk源码(二):String 一个特殊而强大的类! 我的jdk源码(三):AbstractStringBuilder类 我的jdk源码(四):StringBuffer 线程安全可多次修改 我的jdk源码(五):StringBuilder 高效可多次修改String 我的jdk源码(六):Thread 我的jdk源码(七): ThreadLocal 二、 ...

Java觉浅的博客 380

抽丝剥茧聊Kotlin协程之深入理解协程上下文CoroutineContext

1. 前言 如果你对CoroutineContext不了解,本文值得你细细品读,如果一遍看不懂,不妨多读几遍。写作该文的过程也是我对CoroutineContext理解加深的过程。CoroutineContext是协程的基础,值得投入学习 Android开发者对Context都不陌生。在Android系统中,Context可谓神通广大,它可以获取应用资源,可以获取系统资源,可以启动Activity。Context有几个大名鼎鼎的子类,Activity、Application、Service,它们都是应用

御风而行 778

一次线上诡异的“规则不生效”引发的血案——揭秘 HashMap 与多线程的连环坑

这个引发了无数猜测、耗费了大量脑细胞排查的线上事故,到此就彻底复盘结束了。日常业务代码写久了,很多研发同学会不可避免地患上“底层机制盲视症”——看到代码里套了一层,就觉得线程绝对安全了;看到后台监控面板没打出 Error 日志,就盲目自信地觉得代码没抛任何异常。然而,计算机是一门极其严谨的科学,它不会向任何侥幸心理和历史包袱妥协。

weixin_47942629的博客 508

再也不担心问到Java集合了,一文讲透Java中的数据结构

数据结构作为每一个IT人不可回避的问题,而Java对于不同的数据结构提供了非常成熟的实现,这一个又一个实现既是面试中的难点,也是工作中必不可少的工具,在此,笔者经历漫长的剖析,将其抽丝剥茧的呈现出来,在此仅作抛砖引玉,望得诸君高见,若君能有所获在下甚是不亦乐乎,若有迷惑亦与诸君共求之!

刘滨浩的博客 2万+

2024最强Android中高级大厂面试源码秘籍,为你备战2024金三银四,直通大厂

和阅读一本好书一样,阅读源码就是和编程大牛面对面交流的机会,在许多优秀的开源项目中,它们的编码规范和架构设计都是很棒的,另外在设计上也使用了大量的设计模式,通过阅读和学习源码,能够快速提升我们的编码水平,以及对设计模式有更深的理解。如果你没有阅读过源码,面试官可能会对回答的结果不满意,进而导致面试结果不太理想,但如果你对源码有所研究,并能够很好地问答面试官的问题,这可能就是你的加分点,可以形成自己独特的竞争力,吊打面试官,升职加薪不是梦。动态代理和静态代理的区别,是怎么实现的)等,还收纳了很多经典框架。

2401_89821832的博客 762

2021最强Android中高级大厂面试源码秘籍,备战2022直通大厂

前言 很多人都觉得阅读源码是一件比较困难的事情,虽然程序员每天都和代码打交道,但是经过数年的基础教育和职业培训,大部分程序员都会「写」代码,或者至少会抄代码和改代码。 然而,会读代码的并不在多数,会读代码又真正读懂一些大项目的源码的,少之又少。这种怪状,真要追究起来,怪不得程序员这个群体本身 —— 它是两个原因造成的: 我们所有的教育和培训都在强调怎么写代码,并没有教大家如何读代码 大多数工作场景都是一个萝卜一个坑,我们只需要了解一个系统的局部便能开展工作,读不相干的代码,似乎没用 为什么要阅读源码?

datian1234的博客 220

2022 Java八股文核心拆解:从HashMap到JVM与OOM实战

在Java后端开发与求职面试中,基础知识的掌握程度往往决定了技术候选人的上限。无论是集合框架的底层设计、JVM内存模型与垃圾回收机制,还是并发编程中的线程池与锁原理,这些看似零散的知识点,实则是构建完整计算机知识体系的关键拼图。理解HashMap的扩容与树化逻辑、掌握OutOfMemoryError的排查思路、弄清Spring事务失效的常见场景,能帮助开发者从“会写代码”走向“懂为什么这样写”。这些原理不仅支撑着高并发系统的稳定性,也直接应用在线上性能调优与故障诊断中。本文以2022年高频面试题为主线,深入

weixin_31292729的博客 237

超硬核,5w字详解Java集合升级版(推荐收藏)

    Java数据结构实现详解摘要1 集合框架1.1 顶层接口Iterable1.2 Collection接口2 List2.1 List接口2.2 List实现ArrayList2.2.1 ArrayList继承关系2.2.2 ArrayList组成2.2.3 ArrayList构造函数2.2.4 ArrayList中添加元素2.2.4 ArrayList扩容2.2.5 数组copy2.2.6 why?elementData用transient修饰?面试题5:el

刘滨浩的博客 3180
上一篇: LinkedList的抽丝剥茧
下一篇: Java 重载、重写和隐藏的区别
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值