HashMap源码解析

HashMap


初始化设置:

1、初始化默认容量:16

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

2、最大容量:2的30次方,和ArrayList相比ArrayList存放的元素个数多。

static final int MAXIMUM_CAPACITY = 1 << 30;

3、默认加载因子,如果构造方法没有传参的话,默认加载因子就是0.75。

static final float DEFAULT_LOAD_FACTOR = 0.75f;

4、阀值,当一条链上的节点数增加且当增加到节点个数等于TREEIFY_THRESHOLD(10)时,就是将链表变成红黑树来保存。

static final int TREEIFY_THRESHOLD = 8;

5、阈值,当树上的节点削减且当削减到节点个数等于UNTREEIFY_THRESHOLD(6)时,红黑树会塌缩成为链表

static final int UNTREEIFY_THRESHOLD = 6;

6、一个hash节点上树的最小大小是64.

static final int MIN_TREEIFY_CAPACITY = 64;

7、实际存储的key,value的数组,只不过key,value被封装成Node了。

transient Node<K,V>[] table;

HashMap中的内部类——Node:

static class Node<K,V> implements Map.Entry<K,V> {
        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;
        }
}

①注意Node类的成员成员成员变量:hash

  • 这个hash值是根据传来的key,算出各自的hash值,再进行异或处理计算出来的。

存放数据:

1、调用构造方法,让加载因子等于默认的加载因子(0.75)。

 public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; 
}

2、向HashMap中放置一个值

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
}

4、计算key值的hash值,如果key不为空的时候,hash值等于key自身的hash值做异或

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

注意:hash(key)是根据传进来的key值进行计算得出的。

5、放入元素之前的判断:resize()方法

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
            
        threshold = newThr;
        
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab; 
        threshold = newThr;
        
      
        return newTab;
}

这里,由于oldTab(老的数组)此时指向table(null),所以oldCap(老的数组容量)为0,oldThr(老的临界值)也为0,然后让newCap(新的数组容量)为HashMap默认数组容量DEFAULT_INITIAL_CAPACITY(16),newThr(新的临界值)为DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY(默认数组容量乘以默认转换因子:12),然后返回新的节点数组。

  • 其实这个方法就是计算加载因子和阀值,如果当前元素个数达到了阀值,那么就会创建出来新的数组,然后把老的容器中的元素放到新的数组中去。
  • 默认情况下,数组大小为16,那么当hashmap中元素个数超过16×0.75=12的时候,就把数组的大小扩展为2×16=32,即扩大一倍,然后重新计算每个元素在数组中的位置。

6、向容器中放入数据的方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
 
}

这一句的意思是,当容器还是空的时候,插入数据的时候肯定要创建一个容器嘛,然后得到resize()创建的一个长度为16的Node数组。然后我们来看元素插入的那一条语句。

if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

根据key的hash和此时数组长度-1进行与运算,如果数组上此位置为空,那么就将元素直接放入数组中

如果此时数组的元素不为空,那么根据拉链法,就会在原来数组元素上形成链表。

如果链表的长度等于阀值(8)的时候,那么就会形成红黑树来保存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值