HashMap(1.8)源码阅读
Posted gavinyang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap(1.8)源码阅读相关的知识,希望对你有一定的参考价值。
先了解一下用到的位运算符:https://www.cnblogs.com/gavinYang/p/11196492.html
一、初始化
1.无参构造函数:
//负载因子默认值 static final float DEFAULT_LOAD_FACTOR = 0.75f; //指定loadFactor负载因子的值是0.75f public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
2.指定初始化大小和负载因子:
//hashmap的最大容量 static final int MAXIMUM_CAPACITY = 1 << 30; //1左移30位等于1073741824 //initialCapacity:传入的hashmap初始化大小 //loadFactor:负载因子,此时是默认值0.75f 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; this.threshold = tableSizeFor(initialCapacity); } //返回一个大于等于initialCapacity的2的n次方最近的一个值 //假设传入的initialCapacity=5 static final int tableSizeFor(int cap) { int n = cap - 1; //4 n |= n >>> 1; // 00000100 |= 00000010 得出 00000110=6 n |= n >>> 2; // 00000110 |= 00000011 得出 00000111=7 n |= n >>> 4; // 00000111 |= 00000000 得出 00000111=7 n |= n >>> 8; // 00000111 |= 00000000 得出 00000111=7 n |= n >>> 16;// 00000111 |= 00000000 得出 00000111=7 //最终返回n+1=8 return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
3.指定初始化大小(会调用2)
//initialCapacity:hashmap初始化大小 public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); }
二、put元素(转红黑树和put一个TreeNode时待补充)
//插入元素 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } //计算hash值 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } //hashmap的数组 transient Node<K,V>[] table; //存放数据 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; //把table赋值给tab,判断tab是否为空(第一次put时执行) if ((tab = table) == null || (n = tab.length) == 0) //返回数组的长度 n = (tab = resize()).length; //计算一个位置(& 运算后值肯定小于 n-1),如果数组的这个位置不为空则直接放入数据 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //数组不为空,且计算的位置以及有数据 else { Node<K,V> e; K k; //添加的数据与所在的数组的位置的hash值和key相同,则将当前数组位置的数据数据p赋值给e if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //如果数组当前位置的值是TreeNode类型。。。。。。。 else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //死循环,binCount表示链表长度 for (int binCount = 0; ; ++binCount) { //判断当前数组位置是否有下一个值 if ((e = p.next) == null) { //数组位置的下一个值是当前新插入的数据(插入尾部) p.next = newNode(hash, key, value, null); //链表长度大于TREEIFY_THRESHOLD=8时转化为红黑树 if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } //添加的数据与如果下一个值的hash值和key相同,直接跳出 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; //下一个位置的数据赋值给p,循环继续 p = e; } } //key存在,更新新值,返回旧值 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; } //初始化或扩容时调用 final Node<K,V>[] resize() { //将原有的数据存放到oldTab Node<K,V>[] oldTab = table; //旧数组的容量 int oldCap = (oldTab == null) ? 0 : oldTab.length; //需要扩容的大小 int oldThr = threshold; //新的容量,新的需要扩容的大小 int newCap, newThr = 0; //旧数组有数据 if (oldCap > 0) { //旧数组容量大于等于最大容量1 << 30 = 1073741824 if (oldCap >= MAXIMUM_CAPACITY) { //不扩容返回旧数组 threshold = Integer.MAX_VALUE; return oldTab; } //新的容量是旧数组的容量左移以为即:oldCap*2。如果数组容量大于DEFAULT_INITIAL_CAPACITY=16时,扩容的容量也要×2 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } //旧数组为空,进行初始化 else if (oldThr > 0) // 初始化容量设置为threshold(需要扩容的值) newCap = oldThr; else { //无参初始化hashmap时,容量和扩容的大小 newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } //新的threshold(需要扩容的大小)为零时,newThr=newCap * loadFactor if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } //赋值回threshold threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) //定义一个新hashmap的数组,值是新的容量 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) //重新计算hash然后放入新数组的新位置 newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { //数组位置下有链表 Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; //e.hash & oldCap 返回0或者oldCap //如果链表中各节点(e.hash & oldCap)计算值不一样时数据会分布在数组的旧位置和(旧位置+oldCap) if ((e.hash & oldCap) == 0) { //第一次遍历时,loHead等于当前链表 //loTail每循环一次去掉链表头部 if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { //第一次遍历时,hiHead等于当前链表 //hiTail每循环一次去掉链表头部 if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); //将当前数组位置下的链表(hiHead/loHead)放到新数组的(当前数组位置+oldCap)位置 //loTail/hiTail.next设置为空为了防止loTail和loHead(或hiHead和hiTail)指向同一个地址时数据重复, //重复时则清除loHead和hiHead的next值 if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
三、get元素
//返回键对应的值 public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } //计算hash值 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } //根据key获得对应的node 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) { //第一个元素即是要查找的元素 if (first.hash == hash && ((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 { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
四、remove元素
//返回键对应的值 public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; } //计算hash值 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } //value:如果是matchValue则需要传入 //matchValue:如果等于true则还需要匹配值也相等 //movable:如果为false则在删除时不要移动其他节点 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; //数组不等于空,要获取的元素的数组位置不等于空 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; //如果数组当前位置p即是对应的值 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) node = ((TreeNode<K,V>)p).getTreeNode(hash, key); //遍历链表 else { do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break; } p = e; } while ((e = e.next) != null); } } //找到元素,并且matchValue=false或者找到的node的value与指定value相等 if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { if (node instanceof TreeNode) ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); //如果节点在数组上,则直接将数组的位置指定为p的下一个位置 else if (node == p) tab[index] = node.next; //如果节点在链表上,则将当前节点的下一个值指定为找到的节点的下一个值 else p.next = node.next; ++modCount; --size; afterNodeRemoval(node); return node; } } return null; }
以上是关于HashMap(1.8)源码阅读的主要内容,如果未能解决你的问题,请参考以下文章