java基础知识学习总结

xmind文件不知道怎么上传

Map是一个存储key-value键值对的集合,其中Map中的key不允许重复 可以为null,value可以是重复的,key与value一一对应,单看key 是不可重复且无序的集合,就是一个Set集合;单看value是一个可重复且无序的集合,就是一个Collection集合。如图(底层是Entry[])

在HashMap类中定义了初始化容量为DEFAULT_INITIAL_CAPACITY = 1 << 4;的常量

扩容的条件:

  • 当容量达到当前容量与默认加载因子的乘积 并且待插入的位置不为空时 扩容为原来的2倍
  • 当待插入的位置上链表长度超过8 并且数组的容量为超过64时 扩容为原来的2倍

树化条件:

  • 当待插入的位置上链表长度超过8,并且数组的容量超过64 转化为红黑树
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//默认初始化容量16
static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量,
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认加载因子
static final int TREEIFY_THRESHOLD = 8;//树化临界值
static final int UNTREEIFY_THRESHOLD = 6;//反树化临界值
static final int MIN_TREEIFY_CAPACITY = 64;//最小树化容量

首先,在调用无参构造方法时(并没有初始化数组transient Node<K,V>[] table;的长度),执行putVal方法,这里面的hash方法的作用是 为传进来的key值通过hash()方法得出来一个哈希值(因为key是不允许重复的,所以在这里要先得出一个key的哈希值以便后面判断key值是否重复)

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//得到一个尽可能不同的哈希值
    }
//put方法 
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

然后进入putVal()方法,首先声明一个Node类型的tab和p,其中Node类型为Map.Entry的实现类

 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)//把table赋值给tab,第一次添加时由于table没被初始化,所以结果为true进入if
            n = (tab = resize()).length;//执行resize()方法初始化底层数组Entry类型的数组,此方法会返回一个Node数组,把这个数组的长度赋值给n  跳到下一块代码(resize()方法),此时n=16
        if ((p = tab[i = (n - 1) & hash]) == null)//(n - 1) & hash长度-1“与运算”刚才得到的哈希值,其结果一定是[0,n-1],同时把结果赋值给索引 i 然后把tab[i]赋值给p,并判断位置p上是否为空,如果为空,直接添加成功
            tab[i] = newNode(hash, key, value, null);//添加成功
        else {//这是p位置上有数据的情况
            Node<K,V> e; K k;//声明临时变量e(Node类型)  和 k(就是key的类型)
            if (p.hash == hash &&//判断 p 位置上的数据的key的哈希值是否于要加入的数据的key的哈希值是否相等 ,如果不相等 整体返回false,直接跳到else
                ((k = p.key) == key || (key != null && key.equals(k))))//如果相同 继续判断key的equals是否相等 如果相等 就会把原来的数据覆盖
                e = p;//覆盖原来的数据
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {//跳到这里
                for (int binCount = 0; ; ++binCount) {//死循环,取出在此位置(p)上的链表上的所有数据,逐个比较
                    if ((e = p.next) == null) {//如果不存在下一个节点
                        p.next = newNode(hash, key, value, null);//添加成功
                        if (binCount >= TREEIFY_THRESHOLD - 1) // 这里是当链表超过8时转换为红黑树的判断 
                            treeifyBin(tab, hash);//转红黑树的方法
                        break;
                    }
                    if (e.hash == hash &&//逐个判断链表上的数据的key与待加入的数据的key的哈希值比较,如果不相等直接添加成功,并把链表的最后一个数据指向这个数据,如果相同继续判断其equals是否相等,如果相等覆盖掉原来的数据,否则也是添加成功,并把链表的最后一个数据指向这个数据
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
//内部类Node
 final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;//0
        int oldCap = (oldTab == null) ? 0 : oldTab.length;//第一次调用时 返回的结果是0
        int oldThr = threshold;//也是0
        int newCap, newThr = 0;//也是0,所以直接跳到下面else (直接找我标记的②)
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // ② 接上面的调到else处
            newCap = DEFAULT_INITIAL_CAPACITY;//此时底层的数组才被真正的初始化其长度为16;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//这个变量是 map 扩容的临界值 ,当其长度达到这个值的时候就要考虑扩容的问题,但不是立刻就会进行扩容
        }
        if (newThr == 0) {//此时newThr 为16 if语句进不去 直接执行下面
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;//把newThr赋值给threshold =16
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//相当于Node<K,V>[] newTab = (Node<K,V>[])new Node[16];
        table = newTab;//16
        if (oldTab != null) {//第一次oldTab为null,直接跳到最后返回结果newTab
            for (int j = 0; j < oldCap; ++j) {//第二次及以后 遍历数组的每一个位置,放进新数组table
                Node<K,V> e;//把每一个位置上的元素赋值给e
                if ((e = oldTab[j]) != null) {//判断e是否为空
                    oldTab[j] = null;//先把原来的数组 j (j一直变化)位置上的元素置空
                    if (e.next == null)//如果e的下个元素为空,如果为空说明只有一个元素
                        newTab[e.hash & (newCap - 1)] = e;//e.hash & (newCap - 1)计算给e分配的位置,和jdk7中的indexOf方法作用是一样的。
                    else if (e instanceof TreeNode)//判断e是否为红黑树的节点
                        ((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;//把e放在最后
                                else
                                    loTail.next = e;//把e放在最后的下一个
                                loTail = e;//遍历下一个
                            }
                            else {
                                if (hiTail == null)//新的链表尾结点为空,说明链表遍历完了
                                    hiHead = e;//把e放在最后
                                else
                                    hiTail.next = e;//把e放在最后的下一个
                                hiTail = e;//遍历下一个
                            }
                        } while ((e = next) != null);//没有下一个节点时 结束循环
                        if (loTail != null) {//如果扩容前尾结点不为空
                            loTail.next = null;
                            newTab[j] = loHead;//扩容前的头结点 放在 j(上面的 j ) 索引的位置上
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
  newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值