Hashmap源码解析-变量和构造函数(2)

HashMap源码解读系列教程:

介绍重要的Hashmap方法之前,我先将下Hashmap中重要的成员变量和构造函数,以及他们代表的含义。这样在下一讲中结合hashmap的重要函数的讲解会跟容易理解。

1、变量

  1. Hashmap散列表底层数组的默认大小,必须是2的次方数。在这里 1 << 4就是16。这里涉及到位移的运算<<4相当于是乘以16。

    1
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
  2. 散列表数组允许的最大长度,这里默认是1 << 30大约是1073741824

    1
    static final int MAXIMUM_CAPACITY = 1 << 30;
  3. 默认的负载因子,这个概念在上一节中有讲到,就是说允许散列表数组中非空位的比例。一旦超过这个比例(负载因子),那么就会触发散列表的扩容。

    1
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
  4. 散列表中链表的树化阈值,假设链表长度超过了8,那么再新增元素,这个链表将会可能升级为树。

    1
    static final int TREEIFY_THRESHOLD = 8;
  5. 当树的结点数小于这个阈值时,就退化为普通的链表

    1
    static final int UNTREEIFY_THRESHOLD = 6;
  6. MIN_TREEIFY_CAPACITY是另外一个链表升级成树的必要条件,这意味着散列表桶内的元素(链表)数量必须大于TREEIFY_THRESHOLD且散列表的桶的数量大于MIN_TREEIFY_CAPACITY才会触发树化动作。

    1
    static final int MIN_TREEIFY_CAPACITY = 64;
  7. Map中键值对的数量

    1
    transient int size;
  8. 扩容阈值,当上面提的size时大于阈值时,就会触发散列表的扩容。扩容阈值一般是【threshold = 容量*加载因子】

    1
    int threshold;
  9. 散列表的数组

    1
    transient Node<K,V>[] table;
  10. 之前我们谈到散列表中既可以保存链表,也可以在链表长度达到一定程度之后升级为红黑树。那么这个结构的奥妙就在Node对象当中。我们下面看下链表和红黑树的Node对象的结构。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //链表节点
    static class Node<K,V> implements Map.Entry<K,V> {
    //key对应的哈希值
    final int hash;
    //key值
    final K key;
    //value值
    V value;
    //链接到下一个元素。如果已经是链表尾部,则该next为null。
    Node<K,V> next;
    }

    //红黑树节点
    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;
    }

2、构造函数

HashMap提供了4个构造函数,允许你微调一些参数。

  1. 默认的构造函数,负载因子是0.75。

    1
    2
    3
    public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
  2. 该构造函数可以允许指定默认的散列表的槽位数,但是负载因子依然沿用默认值0.75。

    1
    2
    3
    public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
  3. 允许同时指定不同的散列表的槽位数和负载因子。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public HashMap(int initialCapacity, float loadFactor) {
    //槽位数不允许小于零
    if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
    initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
    throw new IllegalArgumentException("Illegal load factor: " +
    loadFactor);
    this.loadFactor = loadFactor;
    //获得散列表数组的长度,该长度必须大于等于给定值且最近的2的整数次幂的数。
    //2的次方数是 2 4 8 16 32 64 ....
    //假设给定的initialCapacity是30,那么返回值是32.
    //此时并未初始化散列表(table)数组,所以把数组长度值暂存在threshold中。等首次put的时候,再进行散列表数组的初始化,同时也会对threshold进行重新计算。因此这里的threshold的计算方法不是capacity*loadFactor。
    this.threshold = tableSizeFor(initialCapacity);
    }
  4. 将一个老的Map,换一个新Map来装,新瓶装旧酒。

    1
    2
    3
    4
    public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
    }

原文链接:https://www.jdkdownload.com/hashmap_2.html