简介 HashMap采用key/value存储结构,每个key对应唯一的value,查询和修改的速度都很快,能达到O(1)的平均时间复杂度。它是非线程安全的,且不保证元素存储的顺序;
 
实战另见 HashMap 
继承体系 
HashMap实现了Cloneable,可以被克隆 
HashMap实现了Serializable,可以被序列化 
HashMap继承自AbstractMap,实现了Map接口,具有Map的所有功能 
 
存储结构 
在Java中,HashMap的实现采用了(数组 + 链表 + 红黑树)的复杂结构,数组的一个元素又称作桶。
在添加元素时,会根据hash值算出元素在数组中的位置,如果该位置没有元素,则直接把元素放置在此处,如果该位置有元素了,则把元素以链表的形式放置在链表的尾部。
当一个链表的元素个数达到一定的数量(且数组的长度达到一定的长度)后,则把链表转化为红黑树,从而提高效率。
数组的查询效率为O(1),链表的查询效率是O(k),红黑树的查询效率是O(log k),k为桶中的元素个数,所以当元素数量非常多的时候,转化为红黑树能极大地提高效率。
源码解析 属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 static  final  int  DEFAULT_INITIAL_CAPACITY = 1  << 4 ;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;transient  Set<Map.Entry<K,V>> entrySet;transient  int  size;transient  int  modCount;int  threshold;final  float  loadFactor;
 
容量 
 
容量为数组的长度,亦即桶的个数,默认为16,最大为2的30次方,当容量达到64时才可以树化。
装载因子 
 
装载因子用来计算容量达到多少时才进行扩容,默认装载因子为0.75。
树化 
 
树化,当容量达到64且链表的长度达到8时进行树化,当链表的长度小于6时反树化。
Node内部类 Node是一个典型的单链表节点,其中,hash用来存储key计算得来的hash值。
1 2 3 4 5 6 static  class  Node <K ,V > implements  Map .Entry <K ,V >  {    final  int  hash;     final  K key;     V value;     Node<K,V> next; } 
 
TreeNode内部类 这是一个神奇的类,它继承自LinkedHashMap中的Entry类,关于LInkedHashMap.Entry这个类我们后面再讲。
TreeNode是一个典型的树型节点,其中,prev是链表中的节点,用于在删除元素的时候可以快速找到它的前置节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static  final  class  TreeNode <K ,V > extends  LinkedHashMap .Entry <K ,V >  {    TreeNode<K,V> parent;       TreeNode<K,V> left;     TreeNode<K,V> right;     TreeNode<K,V> prev;         boolean  red; } static  class  Entry <K ,V > extends  HashMap .Node <K ,V >  {    Entry<K,V> before, after;     Entry(int  hash, K key, V value, Node<K,V> next) {         super (hash, key, value, next);     } } 
 
HashMap()构造方法 空参构造方法,全部使用默认值。
1 2 3 public  HashMap ()   {    this .loadFactor = DEFAULT_LOAD_FACTOR;  } 
 
HashMap(int initialCapacity)构造方法 调用HashMap(int initialCapacity, float loadFactor)构造方法,传入默认装载因子。
1 2 3 public  HashMap (int  initialCapacity)   {    this (initialCapacity, DEFAULT_LOAD_FACTOR); } 
 
HashMap(int initialCapacity)构造方法 判断传入的初始容量和装载因子是否合法,并计算扩容门槛,扩容门槛为传入的初始容量往上取最近的2的n次方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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); } 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 >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1 ; } 
 
put(K key, V value)方法 添加元素的入口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 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<K, V>[] tab;     Node<K, V> p;     int  n, i;          if  ((tab = table) == null  || (n = tab.length) == 0 )                  n = (tab = resize()).length;               if  ((p = tab[i = (n - 1 ) & hash]) == null )                  tab[i] = newNode(hash, key, value, null );     else  {                  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 );                                          if  (binCount >= TREEIFY_THRESHOLD - 1 )                          treeifyBin(tab, hash);                     break ;                 }                                  if  (e.hash == hash &&                         ((k = e.key) == key || (key != null  && key.equals(k))))                     break ;                 p = e;             }         }                  if  (e != null ) {                           V oldValue = e.value;                          if  (!onlyIfAbsent || oldValue == null )                                  e.value = value;                          afterNodeAccess(e);                          return  oldValue;         }     }               ++modCount;          if  (++size > threshold)                  resize();          afterNodeInsertion(evict);          return  null ; } 
 
计算key的hash值;
 
如果桶(数组)数量为0,则初始化桶;
 
如果key所在的桶没有元素,则直接插入;
 
如果key所在的桶中的第一个元素的key与待插入的key相同,说明找到了元素,转后续流程(9)处理;
 
如果第一个元素是树节点,则调用树节点的putTreeVal()寻找元素或插入树节点;
 
如果不是以上三种情况,则遍历桶对应的链表查找key是否存在于链表中;
 
如果找到了对应key的元素,则转后续流程(9)处理;
 
如果没找到对应key的元素,则在链表最后插入一个新节点并判断是否需要树化;
 
如果找到了对应key的元素,则判断是否需要替换旧值,并直接返回旧值;
 
如果插入了元素,则数量加1并判断是否需要扩容;
 
 
resize()方法 扩容方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 final  Node<K, V>[] resize() {         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 >= DEFAULT_INITIAL_CAPACITY)                          newThr = oldThr << 1 ;      } else  if  (oldThr > 0 )                            newCap = oldThr;     else  {                                          newCap = DEFAULT_INITIAL_CAPACITY;         newThr = (int ) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);     }     if  (newThr == 0 ) {                  float  ft = (float ) newCap * loadFactor;         newThr = (newCap < MAXIMUM_CAPACITY && ft < (float ) MAXIMUM_CAPACITY ?                 (int ) ft : Integer.MAX_VALUE);     }          threshold = newThr;          @SuppressWarnings ({"rawtypes" , "unchecked" })     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 )                     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;                                                                           if  ((e.hash & oldCap) == 0 ) {                             if  (loTail == null )                                 loHead = e;                             else                                  loTail.next = e;                             loTail = e;                         } else  {                                                                                       if  (hiTail == null )                                 hiHead = e;                             else                                  hiTail.next = e;                             hiTail = e;                         }                     } while  ((e = next) != null );                                                               if  (loTail != null ) {                         loTail.next = null ;                         newTab[j] = loHead;                     }                                          if  (hiTail != null ) {                         hiTail.next = null ;                         newTab[j + oldCap] = hiHead;                     }                 }             }         }     }     return  newTab; } 
 
如果使用是默认构造方法,则第一次插入元素时初始化为默认值,容量为16,扩容门槛为12; 
如果使用的是非默认构造方法,则第一次插入元素时初始化容量等于扩容门槛,扩容门槛在构造方法里等于传入容量向上最近的2的n次方; 
如果旧容量大于0,则新容量等于旧容量的2倍,但不超过最大容量2的30次方,新扩容门槛为旧扩容门槛的2倍; 
创建一个新容量的桶; 
搬移元素,原链表分化成两个链表,低位链表存储在原来桶的位置,高位链表搬移到原来桶的位置加旧容量的位置; 
 
TreeNode.putTreeVal(…)方法 插入元素到红黑树中的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 final  TreeNode<K, V> putTreeVal (HashMap<K, V> map, Node<K, V>[] tab,                                 int  h, K k, V v)   {    Class<?> kc = null ;          boolean  searched = false ;          TreeNode<K, V> root = (parent != null ) ? root() : this ;          for  (TreeNode<K, V> p = root; ; ) {                           int  dir, ph;                  K pk;         if  ((ph = p.hash) > h) {                          dir = -1 ;         }         else  if  (ph < h)                          dir = 1 ;         else  if  ((pk = p.key) == k || (k != null  && k.equals(pk)))                                       return  p;         else  if  ((kc == null  &&                                  (kc = comparableClassFor(k)) == null ) ||                                  (dir = compareComparables(kc, k, pk)) == 0 ) {                                                                 if  (!searched) {                 TreeNode<K, V> q, ch;                 searched = true ;                                  if  (((ch = p.left) != null  &&                         (q = ch.find(h, k, kc)) != null ) ||                         ((ch = p.right) != null  &&                                 (q = ch.find(h, k, kc)) != null ))                     return  q;             }                          dir = tieBreakOrder(k, pk);         }         TreeNode<K, V> xp = p;         if  ((p = (dir <= 0 ) ? p.left : p.right) == null ) {                          Node<K, V> xpn = xp.next;             TreeNode<K, V> x = map.newTreeNode(h, k, v, xpn);             if  (dir <= 0 )                 xp.left = x;             else                  xp.right = x;             xp.next = x;             x.parent = x.prev = xp;             if  (xpn != null )                 ((TreeNode<K, V>) xpn).prev = x;                                       moveRootToFront(tab, balanceInsertion(root, x));             return  null ;         }     } } 
 
寻找根节点; 
从根节点开始查找; 
比较hash值及key值,如果都相同,直接返回,在putVal()方法中决定是否要替换value值; 
根据hash值及key值确定在树的左子树还是右子树查找,找到了直接返回; 
如果最后没有找到则在树的相应位置插入元素,并做平衡; 
 
treeifyBin()方法 如果插入元素后链表的长度大于等于8则判断是否需要树化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 final  void  treeifyBin (Node<K, V>[] tab, int  hash)   {    int  n, index;     Node<K, V> e;     if  (tab == null  || (n = tab.length) < MIN_TREEIFY_CAPACITY)                                             resize();     else  if  ((e = tab[index = (n - 1 ) & hash]) != null ) {         TreeNode<K, V> hd = null , tl = null ;                  do  {             TreeNode<K, V> p = replacementTreeNode(e, null );             if  (tl == null )                 hd = p;             else  {                 p.prev = tl;                 tl.next = p;             }             tl = p;         } while  ((e = e.next) != null );                  if  ((tab[index] = hd) != null )             hd.treeify(tab);     } } 
 
TreeNode.treeify()方法 真正树化的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 final  void  treeify (Node<K, V>[] tab)   {    TreeNode<K, V> root = null ;     for  (TreeNode<K, V> x = this , next; x != null ; x = next) {         next = (TreeNode<K, V>) x.next;         x.left = x.right = null ;                  if  (root == null ) {             x.parent = null ;             x.red = false ;             root = x;         } else  {             K k = x.key;             int  h = x.hash;             Class<?> kc = null ;                          for  (TreeNode<K, V> p = root; ; ) {                 int  dir, ph;                 K pk = p.key;                 if  ((ph = p.hash) > h)                     dir = -1 ;                 else  if  (ph < h)                     dir = 1 ;                 else  if  ((kc == null  &&                         (kc = comparableClassFor(k)) == null ) ||                         (dir = compareComparables(kc, k, pk)) == 0 )                     dir = tieBreakOrder(k, pk);                                  TreeNode<K, V> xp = p;                 if  ((p = (dir <= 0 ) ? p.left : p.right) == null ) {                     x.parent = xp;                     if  (dir <= 0 )                         xp.left = x;                     else                          xp.right = x;                                          root = balanceInsertion(root, x);                     break ;                 }             }         }     }          moveRootToFront(tab, root); } 
 
从链表的第一个元素开始遍历;
 
将第一个元素作为根节点;
 
其它元素依次插入到红黑树中,再做平衡;
 
将根节点移到链表第一元素的位置(因为平衡的时候根节点会改变);
 
 
get(Object key)方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public  V get (Object key)   {    Node<K, V> e;     return  (e = getNode(hash(key), key)) == null  ? null  : e.value; } 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 ; } 
 
计算key的hash值;
 
找到key所在的桶及其第一个元素;
 
如果第一个元素的key等于待查找的key,直接返回;
 
如果第一个元素是树节点就按树的方式来查找,否则按链表方式查找;
 
 
TreeNode.getTreeNode(int h, Object k)方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 final  TreeNode<K, V> getTreeNode (int  h, Object k)   {         return  ((parent != null ) ? root() : this ).find(h, k, null ); } final  TreeNode<K, V> find (int  h, Object k, Class<?> kc)   {    TreeNode<K, V> p = this ;     do  {         int  ph, dir;         K pk;         TreeNode<K, V> pl = p.left, pr = p.right, q;         if  ((ph = p.hash) > h)                          p = pl;         else  if  (ph < h)                          p = pr;         else  if  ((pk = p.key) == k || (k != null  && k.equals(pk)))                          return  p;         else  if  (pl == null )                          p = pr;         else  if  (pr == null )                          p = pl;         else  if  ((kc != null  ||                 (kc = comparableClassFor(k)) != null ) &&                 (dir = compareComparables(kc, k, pk)) != 0 )                          p = (dir < 0 ) ? pl : pr;         else  if  ((q = pr.find(h, k, kc)) != null )                          return  q;         else                           p = pl;     } while  (p != null );     return  null ; } 
 
经典二叉查找树的查找过程,先根据hash值比较,再根据key值比较决定是查左子树还是右子树。
remove(Object key)方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 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, movable);             else  if  (node == p)                                  tab[index] = node.next;             else                                   p.next = node.next;             ++modCount;             --size;                          afterNodeRemoval(node);             return  node;         }     }     return  null ; } 
 
先查找元素所在的节点;
 
如果找到的节点是树节点,则按树的移除节点处理;
 
如果找到的节点是桶中的第一个节点,则把第二个节点移到第一的位置;
 
否则按链表删除节点处理;
 
修改size,调用移除节点后置处理等;
 
 
TreeNode.removeTreeNode(…)方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 final  void  removeTreeNode (HashMap<K, V> map, Node<K, V>[] tab,                           boolean  movable)   {    int  n;          if  (tab == null  || (n = tab.length) == 0 )         return ;          int  index = (n - 1 ) & hash;          TreeNode<K, V> first = (TreeNode<K, V>) tab[index], root = first, rl;          TreeNode<K, V> succ = (TreeNode<K, V>) next, pred = prev;     if  (pred == null )                  tab[index] = first = succ;     else                   pred.next = succ;          if  (succ != null )         succ.prev = pred;          if  (first == null )         return ;          if  (root.parent != null )         root = root.root();               if  (root == null              || (movable             && (root.right == null              || (rl = root.left) == null              || rl.left == null ))) {         tab[index] = first.untreeify(map);           return ;     }               TreeNode<K, V> p = this , pl = left, pr = right, replacement;     if  (pl != null  && pr != null ) {         TreeNode<K, V> s = pr, sl;         while  ((sl = s.left) != null )              s = sl;         boolean  c = s.red;         s.red = p.red;         p.red = c;          TreeNode<K, V> sr = s.right;         TreeNode<K, V> pp = p.parent;         if  (s == pr) {              p.parent = s;             s.right = p;         } else  {             TreeNode<K, V> sp = s.parent;             if  ((p.parent = sp) != null ) {                 if  (s == sp.left)                     sp.left = p;                 else                      sp.right = p;             }             if  ((s.right = pr) != null )                 pr.parent = s;         }         p.left = null ;         if  ((p.right = sr) != null )             sr.parent = p;         if  ((s.left = pl) != null )             pl.parent = s;         if  ((s.parent = pp) == null )             root = s;         else  if  (p == pp.left)             pp.left = s;         else              pp.right = s;         if  (sr != null )             replacement = sr;         else              replacement = p;     } else  if  (pl != null )         replacement = pl;     else  if  (pr != null )         replacement = pr;     else          replacement = p;     if  (replacement != p) {         TreeNode<K, V> pp = replacement.parent = p.parent;         if  (pp == null )             root = replacement;         else  if  (p == pp.left)             pp.left = replacement;         else              pp.right = replacement;         p.left = p.right = p.parent = null ;     }     TreeNode<K, V> r = p.red ? root : balanceDeletion(root, replacement);     if  (replacement == p) {           TreeNode<K, V> pp = p.parent;         p.parent = null ;         if  (pp != null ) {             if  (p == pp.left)                 pp.left = null ;             else  if  (p == pp.right)                 pp.right = null ;         }     }     if  (movable)         moveRootToFront(tab, r); } 
 
TreeNode本身既是链表节点也是红黑树节点;
 
先删除链表节点;
 
再删除红黑树节点并做平衡;
 
 
总结 
HashMap是一种散列表,采用(数组 + 链表 + 红黑树)的存储结构;
 
HashMap的默认初始容量为16(1<<4),默认装载因子为0.75f,容量总是2的n次方;
 
HashMap扩容时每次容量变为原来的两倍;
 
当桶的数量小于64时不会进行树化,只会扩容;
 
当桶的数量大于64且单个桶中元素的数量大于8时,进行树化;
 
当单个桶中元素数量小于6时,进行反树化;
 
HashMap是非线程安全的容器;
 
HashMap查找添加元素的时间复杂度都为O(1);
 
 
彩蛋 红黑树知多少? 
红黑树具有以下5种性质:
节点是红色或黑色。
 
根节点是黑色。
 
每个叶节点(NIL节点,空节点)是黑色的。
 
每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
 
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
 
 
红黑树的时间复杂度为O(log n),与树的高度成正比。
红黑树每次的插入、删除操作都需要做平衡,平衡时有可能会改变根节点的位置,颜色转换,左旋,右旋等。