put—第一次put一个值的过程
-
说点杂的
首先需要注意的是,我们所说的hashmap的默认容量为16,并不是指的实例化后的hashmap容量为16,实例化后的hashmap没有任何值设置(虽然有默认值,但是数组啥的都还没创建没任何意义)。
实例化一个hashmap,debug后发现map1并没有任何的属性。
Map<String,Object> map1 = new HashMap<>();
-
put一个值的过程
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
我们来看一下put方法
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
第一步获取hash码(注意这个hash码是用来)
首先调用了hash(key)来计算hash值。如果是key是空,hash码就是0,否则先将key的hash码右移16位补零,再进行^运算得到hash值。(key的hash码根据key的类型而不同,这里是string,则调用string类的hashcode方法)static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
第二步调用putval方法存值
首先看一下属性
//这里hash已经算出,key, value就是我们传入的键值对 putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) //putval()方法内 //底层实现用的数组就是这个tab,p是具体的存放的值,n为数组长度,i为数组下标 //node节点属性(final int hash;final K key;V value;Node<K,V> next;) Node<K,V>[] tab; Node<K,V> p; int n, i; //putval()方法内 //(第一次调用,tab为null,调用resize进行数组的初始化) if ((tab = table) == null || (n = tab.length) == 0) //请先记住这个n最终值为16 n = (tab = resize()).length; ----------------------------------------- //进入resize()方法内 Node<K,V>[] oldTab = table;//初始为null // 由于oldTab=null, oldcap容量为0 int oldCap = (oldTab == null) ? 0 : oldTab.length; // 0 // oldThr阈值为0 int oldThr = threshold; int newCap, newThr = 0; //旧容量比0大的时候 if (oldCap > 0) { ... } else if (oldThr > 0) // initial capacity was placed in threshold ... else { // zero initial threshold signifies using defaults //都为0的时候,也就是第一次进来,设置新容量为DEFAULT_INITIAL_CAPACITY也就是16 //设置新的阈值为DEFAULT_LOAD_FACTOR*DEFAULT_INITIAL_CAPACITY 也就是0.75*16=12 newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } //设置当前阈值为新阈值的值,12 threshold = newThr; //实例化node数组,数组大小为newCap,16 这里数组的初始化创建就完成了,也就是n为什么为16 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; --------------------------------- //回到putval() // (n - 1) & hash算出下标,i就等于这个值,n-1是最大下标保证了&后的值不会超出这个数 // p就是对应下标的node节点,也就是tab[i], p为null说明该下标没有hash碰撞 // 直接设置当前tab[i]的node信息为 hash, key, value(也就是我们一开始传入的值) if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { //这里会根据hash碰撞个数的大小分为,链表跟红黑树 第一次put用不到 //第一次put的时候还只是个node类型的数组!!!!! ... } return null;
到了这,第一个put就完成了