JDK源码-HashMap
一、HashMap的数据结构?为什么要采用这样的数据结构?
//默认 HashMap 集合初始容量为16(必须是 2 的倍数) static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 //集合的最大容量,如果通过带参构造指定的最大容量超过此数,默认还是使用此数 static final int MAXIMUM_CAPACITY = 1 << 30; //默认的填充因子 static final float DEFAULT_LOAD_FACTOR = 0.75f; //当桶(bucket)上的结点数大于这个值时会转成红黑树(JDK1.8新增) static final int TREEIFY_THRESHOLD = 8; //当桶(bucket)上的节点数小于这个值时会转成链表(JDK1.8新增) static final int UNTREEIFY_THRESHOLD = 6; /**(JDK1.8新增) * 当集合中的容量大于这个值时,表中的桶才能进行树形化 ,否则桶内元素太多时会扩容, * 而不是树形化 为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD */ static final int MIN_TREEIFY_CAPACITY = 64;
//hash(key)获取Key的哈希值,equls返回为true,则两者的hashcode一定相等,意即相等的对象必须具有相等的哈希码。 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } // static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { //创建Node数组 Node<K,V>[] tab; Node<K,V> p; int n, i; //如果table为null,扩容resize() if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //(n - 1) & hash按位与运算快,除法运算效率低 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);//tab[i] 为null,直接将新的key-value插入到计算的索引i位置 else { //否则tab[i] 不为null,表示该位置已经有值了。如果key相同,直接覆盖value值,==和equals判断 Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //如果是红黑树 else if (p instanceof 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); //当长度大于8转化为红黑树 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((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; }
JDK1.8前是链表+数组,JDK1.8后是链表+数组+红黑树。从put方法进去看,创建Node<key,value>[] table数组,默认长度16(2的幂次方,方便后面的与运算,hash值计算),LOAD_FACTOR负载因子默认0.75(存放Node超过8时会自动扩容两倍)。
key经过hash()运算,返回最大程度的每次hash值不同,进行右移运算,让高位参与运算,减少hash碰撞,随后通过indexFor(h,length)定位到数组下标(h & length-1),与运算和去取模运算得出结果一致,与运算执行效率高。
为什么使用红黑树?平衡二叉树?
头发越少,我越强

浙公网安备 33010602011771号