HashMap是一种K-V映射的一种数据结构,通过K(key)值能实现在O(1)的时间复杂度下找到对应的V(value)。JDK1.8之前,HashMap的底层数据结构是数组+链表,数组中的每个元素称为一个Entry,包含(hash,key,value,next)这四个元素,其中链表是用来解决碰撞(冲突)的,如果hash值相同即对应于数组中同一一个下标,此时会利用链表将元素插入链表的尾部,(JDK1.8是头插法)。在JDK1.8及之后,底层的数据结构是:数组+(链表,红黑树),引入红黑树是为了避免链表过长,影响元素值的查找,因此当整体的数组大小大于64时,并且链表的长度大于或等于8时,会把链表转化成红黑树。在HashMap这一数据结构中,常见的方法有get、put等,在查找或者插入元素时都会建立临时数组和指针。常见的Map有:HashMap、TreeMap、LinkedHashMap、HashTable、concurrentHashMap等。掌握HashMap对于面试时很有帮助的,这是面试常问的知识点。
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; } //getKey public final K getKey() {return key;} public final V getValue() {return value;} public final String toString() {return key + "=" + value;} //重点,面试常备问道,求hashCode的值是key和value的异或 public final int hashCode(){return Objects.hashCode(key)^Objects.hashCode(value);} //需要暂存原始值,最后再返回 public final V setValue(V newValue){ V oldValue = value; value = newValue; return oldValue; } //需要重写equals,当key,value同时相等时,才相等 public final boolean equals(Object o){ if(o == this) return true; if(o instanceof Map.Entry){ Map.Entry<?,?> e = (Map.Entry<?,?>) o; if(Objects.equals(key,e.getKey()) && Objects.equals(value,e.getValue())) return true; } return false; } //hash是key值的hashcode高低16位异或,从这里可以知道jdk1.8hashmap的key是可以为null //若为null,取0,hashtable中的key是不能为null的 static final int hash(Object key){ int h; return (key == null)?0:(h = key.hashCode()^(h>>>16)); } //给出一个初始容量,会根据给定的初始容量,给出最近的2的多少平方 static final int tableSizeFor(int cap){ int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMU_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } //同上,另一种实现方法,得到最近的2的多少的平方 static final int tableSizeFor(int cap){ int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1); return (n < 0) ? 1 : (n >= MAXIMU_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } //其中numberOfLeadingZeros方法,采用了从大到小进行判断 public static int numberOfLeadingZeros(int i){ if(i <= 0){ return i == 0 ? 32 : 0; } int n = 31; if(i >= 1 << 16) {n -= 16; i >>>= 16;} if(i >= 1 << 8) {n -= 8; i >>>= 8;} if(i >= 1 << 4) {n -= 4; i >>>= 4;} if(i >= 1 << 2) {n -= 2; i>>>= 2;} return n - (i >>> 1); } //初始化HashMap public HashMap(int initialCapacity, float loadFactor){ if(initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity:" + initialCapacity); if(initialCapacity > MAXIMUM_CAPACITY){ initalCapacity = MAXIMUM_CAPACITY; } if(loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshlod = tableSizeFor(initialCapacity); } //获取特定key的value值 public V get(Object key){ Node<K,V> e; return (e = getNode(hash(key),key)) == null ? null : e.value; } //通过hash、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);//进行循环,一直比对hash、key是否相等 } return null; } //利用getNode进行判断 public boolean containsKey(Object key){return getNode(hash(key),key) != null;} //put--(key,value) public V put(K key, V value){ return putVal(hash(key),key,value,false,true); } //重点来看看putVal 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数据为空,进行扩容 n = (tab = resize()).length; if((p = tab[i = (n - 1)&hash]) == null) //若找到下标,此时没有值,即为null,则创建结点 tab[i] = newNode(hash,key,value,null); else{//否则将将进行遍历链表 Node<K,V> e; K k; //先检查头结点,如果hash,key相等,则已经插入了该结点 if(p.hash == hash && ((k = e.key) == key) || (key != null && key.equals(k))) e = p; //判断插入的结点p是否是树结点 else if(p isinstanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeval(this,tab,hash,key,value); else{ for(int binCount = 0;;++binCount){ if((e = p.next) == null){ p.next = newNode(hash,key,value,null); //如果binCount>=7时,链表树化为红黑树 if(binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab,hash); break; }//找到相等的结点,直接break if(e.hash == hash && ((k = e.key) == key ||(key != null && key.equals(k)))) break; //移动链表指针,指向下一个结点 p = e; } } if(e != null){//存在映射关系,但是value为空 V oldValue = e.value; if(!onlyIfAbsent || oldValue == value) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if(++size > threshold) resize(); afterNodeInsertion(evict); return null; } final Node<K,V>[] resize(){ //定义oldTab,oldThr Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0:oldTab.length; int oldThr = threshold; int newCap, newThr = 0;//新的容量、新的阈值 //进行判断 if(oldCap > 0){ //直接把阈值设置为最大值,返回原来的数组 if(oldCap >= MAXIMUM_CAPACITY){ threshold = Integer.MAX_VALUE; return oldTab; }//容量放大两倍,阈值也放大两倍 else if((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFALUT_INITIAL_CAPACITY) newThr = oldThr << 1; // 阈值放大两倍 } //此时,oldCap等于零,但是阈值oldThr大于零,直接用oldThr进行替换 else if(oldThr > 0) newCap = oldThr;//用thre替换初始容量 else {//此时,oldCap和oldThr都为零,进行初始值替换,表明第一次扩容 newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACOR * DEFAULT_INITIAL) } //初始化阈值newThr,初始化threshold if(newThr == 0){ float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY)?(int)ft : Integer.MAX_VALUE; } threshold = newThr; //建立Node型数组,对table进行赋值 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; //将oldTab中元素赋值给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) //判断原来数组位置是否只有一个节点,则进行赋值 newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode)//判断是树节点 ((TreeNode<K,V>)e).split(this,newTab, j,oldCap); else{ //rehash,高位等于索引+oldCap,即:j + oldCap; 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; else loTail.next = e; loTail = e; }//表明需要从新hash到新的位置,同理如上 else{ if(hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } }while((e = next) != null); if(loTail != null){ //先建立一个链表,之后将这个链表接到j索引处 loTail.next = null; newTab[j] = loHead; } // 同理只是更改了索引位置:j + hiHead if(hiTail != null){ hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; } public V remove(Object key){ Node<K,V> e; return (e = removeNode(hash(key),key,null,false,true)) == null ? null : e.value; } 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; //若是头结点 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); } } if(node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))){ if(node instanceof TreeNode) ((TreeNode<K,V>)node).removeTreeNode(this,tab,movale); //若判断是头结点,直接去掉头结点,接在后面 else if(node == p) tab[index] = node.next; else p.next = node.next;//在链表中间,跳过该节点 ++modCount; --size; afterNodeRemoval(node); return node; } } return null; } }HashMap源码(JDK1.8)-手动注释
内容版权声明:除非注明,否则皆为本站原创文章。