JAVA数据结构篇--5理解HashMap

前言:Map 作为key-value 数据结构存放数据,它的内部结构是怎么的,它的存放和获取又是怎样的;本文对jdk8 库中对HashMap的数据存放进行探究。

1 使用:

// 声明
Map<String,Object> map = new HashMap(10, (float) 0.8);
// 放入元素
map.put("key","value");
map.put("key","123");
// 获取元素
map.get("key");
// 移除元素
map.remove("key");
// 元素的数量
map.size();
// entry 遍历
Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator();
while (it.hasNext()) {
   Map.Entry<String, Object> entry = it.next();
   System.out.println("key:" + entry.getKey() + " "
           + "Value:" + entry.getValue());
}

2 过程:
参数:

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    // 默认初始容量 16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

    // 最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30;

    // 默认负载因子 0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 当链表中的元素个数大于等于 8,并且数组的长度大于等于 64 时将链表转为红黑树
    static final int TREEIFY_THRESHOLD = 8;

    // 当链表中的元素个数大于等于 8,并且数组的长度大于等于 64 时将链表转为红黑树
    static final int MIN_TREEIFY_CAPACITY = 64;

    // 当红黑树的长度小于 6 时转为链表
    static final int UNTREEIFY_THRESHOLD = 6;

    // 第一次使用时,才进行初始化操作
    transient Node<K,V>[] table;
  
    // 阈(yu)值,由负载因子和容量决定:CAPACITY * LOAD_FACTOR,默认为 16 * 0.75 = 12
    // 当哈希桶数组内的节点数大于该值时,则扩容
    int threshold;
	/** tab 数组的长度
	  * The number of key-value mappings contained in this map.
	  */
	 transient int size;

 /**
  * The number of times this HashMap has been structurally modified
  * Structural modifications are those that change the number of mappings in
  * the HashMap or otherwise modify its internal structure (e.g.,
  * rehash).  This field is used to make iterators on Collection-views of
  * the HashMap fail-fast.  (See ConcurrentModificationException).
  */
 transient int modCount;
}

2.1 new HashMap:

Map<String, Object> map = new HashMap(20);
// 无参构造
public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
// 指定初始容量
public HashMap(int initialCapacity) {
   this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
// 指定集合转化为 Map
public HashMap(Map<? extends K, ? extends V> m) {  
   this.loadFactor = DEFAULT_LOAD_FACTOR;  
   putMapEntries(m, false);  
}
// 指定初始容量和加载因子
public HashMap(int initialCapacity, float loadFactor) {
	// 对初始化数组的长度和负载因子进行校验
   if (initialCapacity < 0)// 初始化长度不能为0
       throw new IllegalArgumentException("Illegal initial capacity: " +
                                          initialCapacity);
   if (initialCapacity > MAXIMUM_CAPACITY)// 长度超过int 32 位的最大长度
       initialCapacity = MAXIMUM_CAPACITY;// 取最大长度
   if (loadFactor <= 0 || Float.isNaN(loadFactor))// 负载因子数字判断
       throw new IllegalArgumentException("Illegal load factor: " +
                                          loadFactor);
   this.loadFactor = loadFactor;// 赋值负载因子
   this.threshold = tableSizeFor(initialCapacity);// 扩容阈值 初始化下一次需要扩容时 的长度
}
/**  返回一个大于 cap 的最小的 2 的 n 次幂,比如 cap=100,则返回 128。 cap=16,则返回 16
  * Returns a power of two size for the given target capacity.
  */
 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;
 }

2.2 put(K key, V value) :

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
//  得到key 的hash 数值
static final int hash(Object key) {
    int h;
    // k 的hashCode 与k 的hashCode的高16位进行异或
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
 * Implements Map.put and related methods
 *
 * @param hash hash for key
 * @param key the key
 * @param value the value to put
 * @param onlyIfAbsent if true, don't change existing value
 * @param evict if false, the table is in creation mode.
 * @return previous value, or null if none
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    // 变量声明数组 tab = null; p= null; n;i
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 将table 赋值给tab ;将tab 的长度赋值给n
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;// tab 长度为空,说明没有进行初始化,需要进行初始化
     //   获得对应tab 的位置坐标,并将改位置的值赋值给p 
     // 如果p 节点为null ,则表示改tab 位置没有被元素占用可以直接放入数组中
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);// 初始化node 节点 ,将改节点放入tab 数组中
    else {// 如果发现tab 数组中改位置已经有元素占用
    	// 声明e = null ; k
        Node<K,V> e; K k;
        // 如果tab 位置中元素的hash 值和要放入元素的hash 值相同并且
        // 此时tab位置中的元素和将要放入的元素key 是相同的,则将tab位置中的元素 赋值给e 
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
         // 如果发现tab 位置中元素和要放入的元素key 不是同一个,
          // 并且 tab 位置中元素 是树结构则进入树结构进行节点的存入
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {// 否则为链表结构则进入链表结构数据的存入
        		// 没有循环结束条件的 for
            for (int binCount = 0; ; ++binCount) { 
            	// 将tab 位置中元素的下一个节点 赋值给e,
            	//  如果发现下一个节点为null ,则可以将新的node 加点加入到链表中
                if ((e = p.next) == null) {
                   // 初始化节点并放入到单向链表中
                    p.next = newNode(hash, key, value, null);
                    // 如果链表的长度大于等于7
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 
                        treeifyBin(tab, hash);// 进入链表转换为红黑树的逻辑
                    break;
                }
                // 如果 e 节点的key 和要存入node 节点的key 是相同的,
                // 则跳出循环(后续将value 直接替换 到原有node 节点的value)
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;// 等价于 p=p.next;
            }
        }
        // 如果e 不为null 则在原有的结构中 找到了与本次要存入key 相同的node 节点,
        // 则执行替换操作
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;// 替换完成返回本次要存入的value
        }
    }
    ++modCount;// modCount 自增
    // 将元素的长度+1 ;如果增加元素后发现 长度已经达到扩容的条件
    if (++size > threshold)
        resize();// 进行扩容
    afterNodeInsertion(evict);
    return null;
}
 // Create a regular (non-tree) node
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
    return new Node<>(hash, key, value, next);
}
Node(int hash, K key, V value, Node<K,V> next) {
    this.hash = hash;
    this.key = key;
    this.value = value;
    this.next = next;
}

resize() table 的初始化或者数组达到扩容需求:
此方法用来对hashMap进行 初始化(如果没有进行过初始化),或者数组长度达到阈值时进行扩容

final Node<K,V>[] resize() {
	// table 数组赋值给 oldTab 
    Node<K,V>[] oldTab = table;
    // 如果原有的table 是null 则返回0 ,否则返回 原有的table 长度;
    //  第一次初始化时 oldTab为null ,oldCap =0
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    // 将扩容的阈值赋值给oldThr 
    int oldThr = threshold;
    // 声明newCap = 0;newThr =0
    int newCap, newThr = 0;
    if (oldCap > 0) {// table 已经被初始化即扩容时进入
    // 如果 之前的table 数组长度已经大于int 最大值
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;// 扩容阈值赋值为int 的最大值
            return oldTab;// 直接返回原有的 table
        }
        // 将原有的table 的长度赋值给 newCap ,左移一位(相当于*2)
       //  即新的数组长度比老的长度扩大一倍
        // table 长度大于等于 16
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            // 将原有的扩容阈值 *2 赋值给newThr 
            newThr = oldThr << 1; // double threshold
    }
    // 如果扩容的阈值大于0 
    else if (oldThr > 0) // initial capacity was placed in threshold 
        newCap = oldThr;// 将阈值赋值给newCap 
    else {               // zero initial threshold signifies using defaults
    	// 如果原有的阈值为0 则进行初始化
        newCap = DEFAULT_INITIAL_CAPACITY;// 数组新的长度 newCap = 16
        // 新的阈值newThr = 0.75*16= 12
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
	// 如果阈值为0,初始化阈值
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;// ft
        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
    table = newTab;
    // ,第一次初始化时oldTab  为null;
    // 后续如果达到数组长度扩容时需要对之前table 的数据进行转移
    if (oldTab != null) {
      // 此时oldCap 已经进行一次左移操作(相当于*2)
        for (int j = 0; j < oldCap; ++j) {
        	// 声明e =null
            Node<K,V> e;
            // 依次将算作的元素赋值给 e 
            if ((e = oldTab[j]) != null) {
            	// 如果e 不为null 则代表有数据,需要向新的数组转移
                oldTab[j] = null;// 将oldtable 改位置的元素置空
                // 如果e 节点的下一个节点为null ,则证明改数组位置只有一个节点
                if (e.next == null) 
                   // 在新的newTab 重新计算数组下标并赋值
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)// 如果该位置是树节点
                	// 树节点数据的转移
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order  该位置是链表结构
                	// 声明  loHead = null, loTail = null
                    Node<K,V> loHead = null, loTail = null;
                    // 声明   hiHead = null, hiTail = null
                    Node<K,V> hiHead = null, hiTail = null;
                    // 声明next = null
                    Node<K,V> next;
                    do {
                    	// 链表的下个节点
                        next = e.next;
                        //  链表节点的hash 对扩容后的2倍oldCap 取与,
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)// 初始loTail 为null
                                loHead = e;// 将e 节点赋值给loHead  节点
                            else
                                loTail.next = e;
                            loTail = e;// 将e 节点赋值给loTail
                        }
                        else {//   链表节点的hash 对 之前table的length 取模下标不为0
                            if (hiTail == null)
                                hiHead = e;// hiHead 执行e
                            else
                                hiTail.next = e;
                            hiTail = 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;
}

扩容时红黑树的处理:
split(this, newTab, j, oldCap):

/**
** map HashMap 自身, tab 新的数组,index 当前数组下标,bit 扩容后的数组长度
**/
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
	// 将数组的e 节点 赋值给b 
    TreeNode<K,V> b = this;
    // Relink into lo and hi lists, preserving order
    // 声明节点
    TreeNode<K,V> loHead = null, loTail = null;
    TreeNode<K,V> hiHead = null, hiTail = null;
    int lc = 0, hc = 0;
    // 将节点赋值给e ,声明next 。循环条件 e不为null;下次遍历,将next 赋值给e
    // e.hash & bit) == 0 构建为条件;构建出2个双向链表
    for (TreeNode<K,V> e = b, next; e != null; e = next) {
    	// 获取节点的下一个节点
        next = (TreeNode<K,V>)e.next;
        // 下一个节点 置null
        e.next = null;
        // 计算e。hash 的数组下标
        if ((e.hash & bit) == 0) {
           // loTail 赋值给e 的前置节点,第一次 e.prev = null
            if ((e.prev = loTail) == null)
                loHead = e;// loHead节点指向e
            else
                loTail.next = e;
            loTail = e;// loTail  指向e
            ++lc;
        }
        else {//  计算e。hash 的数组下标 不为0
             // hiTail赋值给e 的前置节点,第一次 e.prev = null
            if ((e.prev = hiTail) == null)
                hiHead = e;
            else
                hiTail.next = e;
            hiTail = e;// hiTail 指向e
            ++hc;
        }
    }
     // loHead 不为null 说明此双向链表是有元素的
    if (loHead != null) 
        if (lc <= UNTREEIFY_THRESHOLD)// 元素的长度如果小于等于6
           // 转换为单向链表,并赋值到原有数组下标的位置
            tab[index] = loHead.untreeify(map);
        else {// 如果元素长度大于6
            tab[index] = loHead;// 将首节点 放入tab 数组中
            if (hiHead != null) // (else is already treeified)
                loHead.treeify(tab);// 重新构建红黑树
        }
    }
    // 散列到扩容后的数组位置 hiHead  不为null
    if (hiHead != null) {
        if (hc <= UNTREEIFY_THRESHOLD)// 长度小于等于6 构建单向链表
            tab[index + bit] = hiHead.untreeify(map);
        else {
            tab[index + bit] = hiHead;//  构建红黑树
            if (loHead != null)
                hiHead.treeify(tab);
        }
    }
}
/**
 * Returns a list of non-TreeNodes replacing those linked from
 * this node.
 */
final Node<K,V> untreeify(HashMap<K,V> map) {
	// 声明hd 首节点为null,tl  尾节点指针为null
    Node<K,V> hd = null, tl = null;
    // 循环,开始条件为双向链表首节点,
    // 条件为q 节点不为null,依次对双向链表向后遍历
    for (Node<K,V> q = this; q != null; q = q.next) {
    	// 构建 node 节点	
        Node<K,V> p = map.replacementNode(q, null);
        // 第一次tl 为null 随后循环tl不为null
        if (tl == null)
            hd = p;// 将当前构建的p 节点赋值给hd 节点
        else
            tl.next = p;// 将尾节点的下一节点指向新创建的p 节点
        tl = p;// 将构建的p 节点赋值给tl
    }
    // 返回链表首节点
    return hd;
}
Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
    return new Node<>(p.hash, p.key, p.value, next);
}
Node(int hash, K key, V value, Node<K,V> next) {
    this.hash = hash;
    this.key = key;
    this.value = value;
    this.next = next;
}

当我们不断的向Map 中放入元素,当table 中某个数组位置的链表长度 大于等于7 时 进入 treeifyBin(tab, hash) 进入链表转红黑树 :

final void treeifyBin(Node<K,V>[] tab, int hash) {
	// 声明 n ,index ,声明e 节点
    int n, index; Node<K,V> e;
    // 如果没有进行初始化,需要先进初始化并返回;
    // 或者链表的长度大于7但是数组的长度小于64  也进行resize()
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
    	// 链表的长度大于7 并且数组的长度大于等于64
    	// 得到tab 数组对应坐标下的节点并赋值给e,如果节点不为空
    	// 声明hd,tl 节点
        TreeNode<K,V> hd = null, tl = null;
        // node 双向链表构建
        do {
        	// 初始化 treeNode 
            TreeNode<K,V> p = replacementTreeNode(e, null);
            // 第一次tl 为null,之后循环tl 为上一次的treeNode 节点
            if (tl == null)
                hd = p; // 初始的p 节点赋值给hd
            else {
                p.prev = tl;// 将当前节点的前置节点指向上一节点
                tl.next = p;// 上一节点的下一节点指向p
            }
            tl = p;// 将p 赋值给tl
        } while ((e = e.next) != null);
        // 将链表中的下一个节点赋值给e,并且e 不为null 进入下一次循环
        
        // 双向链表构架完成 hd 指向首节点
        // 将首节点放入到 tab 数组中 ,并返回链表首节点
        if ((tab[index] = hd) != null)
            hd.treeify(tab);// 构建树形结构
    }
}
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
   return new TreeNode<>(p.hash, p.key, p.value, next);
}
TreeNode<K,V> parent;  // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev;    // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
    super(hash, key, val, next);
}
Entry(int hash, K key, V value, Node<K,V> next) {
    super(hash, key, value, next);
}
Node(int hash, K key, V value, Node<K,V> next) {
    this.hash = hash;
    this.key = key;
    this.value = value;
    this.next = next;
}

treeify(tab) 树形结构构建(并没有将原有的双向链表结构打断):

 /** 将构建好的双向链表节点转换为红黑树,注意转换完成之后,双向链表中节点之间的prev 和next 关系依然存在
* Forms tree of the nodes linked from this node.
* @return root of tree
*/
final void treeify(Node<K,V>[] tab) {
   // 声明root 节点
   TreeNode<K,V> root = null;
   // 循环双向链表,初始值x = 链表的首节点;
   // 循环终止条件 x 等于null;x=next 依次向后遍历
   for (TreeNode<K,V> x = this, next; x != null; x = next) {
   		// 获取链表的下一个节点
       next = (TreeNode<K,V>)x.next;
       // 声明 x 节点的做节点为x本身,右节点 为null
       x.left = x.right = null;
       // 第一次循环 root 为null,后续循环 root 不为null
       if (root == null) {
           x.parent = null;// 将x 节点的父节点 置null
           x.red = false;// x 的颜色为黑色
           root = x;// 将x 节点赋值给root 节点
       }
       else {
       	   // 得到节点的key
           K k = x.key;
           // 得到节点的hash
           int h = x.hash;
           // 声明kc 为null
           Class<?> kc = null;
           // 没有循环终止条件的for 将当前x 节点放入到 treeNode 中
           for (TreeNode<K,V> p = root;;) {
           		// 循环开始 p 为root 节点
           		// 声明dir ,ph
               int dir, ph;
               // 得到 p 的key
               K pk = p.key;
               // 如果p 节点的hash 值要大于 本次要加入的x 的hash 值
               if ((ph = p.hash) > h)
                   dir = -1;//  
               else if (ph < h)
                   dir = 1;//  如果p 节点的hash 值要小于 本次要加入的x 的hash 值
               else if ((kc == null &&
                         (kc = comparableClassFor(k)) == null) ||
                        (dir = compareComparables(kc, k, pk)) == 0)
                  //  如果p 节点的hash 值要等于 本次要加入的x 的hash 值
                   dir = tieBreakOrder(k, pk);
				// 将p 节点赋值xp 。第一次循环 xp 为root 节点
               TreeNode<K,V> xp = p;
               // 判断要放在p 的位置 dir小于等于0 p的左子节点赋值给p ,
               // 否则将右子节点赋值给p
               // 如果节点为空代表没有被元素占用可以放入,否则进入下一次循环
               if ((p = (dir <= 0) ? p.left : p.right) == null) { 
               	   // 当前要放入的节点的父节点 指向xp
                   x.parent = xp;
                   if (dir <= 0)// 放入xp 的左子节点 
                       xp.left = x;
                   else // 放入xp 的右子节点
                       xp.right = x;
                   // 放入成功进行重平衡,插入和删除节点的时候通过旋转和改变节点颜色,
                   // 让其符合红黑树的定义
                   root = balanceInsertion(root, x);
                   break; // 跳出循环
               }
           }
       }
   }
   // 确保root 节点是tab 数组中元素位的首节点
   moveRootToFront(tab, root);
}
/**
 * Ensures that the given root is the first node of its bin.
 */
static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
    int n;
    if (root != null && tab != null && (n = tab.length) > 0) {
        int index = (n - 1) & root.hash; // root  的table 下标位置
        // 得到改位置的节点
        TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
        if (root != first) {// 如果改位置不为root 
        	// 声明rn 节点
            Node<K,V> rn;
            // 将root 节点放入改位置
            tab[index] = root;
            //  获取root 节点的前置节点
            TreeNode<K,V> rp = root.prev;
            // root 节点的下一个节点不为空
            if ((rn = root.next) != null)
                ((TreeNode<K,V>)rn).prev = rp;
                // 将root 下一个节点的前置节点指向 root 的前置节点
            if (rp != null)// 前置节点不为null
                rp.next = rn; 
                // 前置节点的下一节点指向 root 的下一节点 将root 节点从链表中剔除
            if (first != null)// 如果first 节点不为null
                first.prev = root; // 将first 节点的前置节点置为 root
            root.next = first;// 将root 节点的下一节点置为 first
            root.prev = null;// 将root 的前置节点 置为null
        }
        // 校验
        assert checkInvariants(root);
    }
 }

当发生hash 冲突且table数组中改位置的元素为树形节点则将改节点放入到红黑树中:
putTreeVal(this, tab, hash, key, value):

/** 
* map hashMap 本身,tab node 数组,h 为当前放入key-value 的key hash  ,k 为key, v 为value
 * Tree version of putVal.
 */
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;
    // root 根节点赋值
    TreeNode<K,V> root = (parent != null) ? root() : this;
    for (TreeNode<K,V> p = root;;) {// 循环插入节点
    	// 声明
        int dir, ph; K pk;
        // 将p 节点的hash 赋值给 ph
        if ((ph = p.hash) > h)// 要插入的key hash 小于 p 的hash
            dir = -1;
        else if (ph < h)// 要插入的key hash 大于 p 的hash
            dir = 1;
         // 要插入的key hash 等于 p 的hash 并且key 是相同的,则直接返回改节点
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;
        // 要插入的key hash 等于 p 的hash  
        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))
                     // 遍历p的左子树和右子树 查找改相同的key 如果找到直接返回改节点 
                    return q;
            }
            dir = tieBreakOrder(k, pk);
        }
		// p 节点赋值 给xp
        TreeNode<K,V> xp = p;
        // 如果dir <=0 将p的做子节点赋值给p ,否则将右子节点赋值给p ,
        // 如果p 为null 则代表改位置没有被占用可以放入元素
        if ((p = (dir <= 0) ? p.left : p.right) == null) {
        	// 将节点插入到红黑树中,同时将该节点 插入到 p 和 p.next 双向链表的中间位置
        	// p的 next 节点赋值给xpn
            Node<K,V> xpn = xp.next;
            //  初始化treeNode 节点
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
            if (dir <= 0)// 放入p 的左子节点
                xp.left = x;
            else// 放入p 的右子节点
                xp.right = x;
            // 将x 节点插入到p 和p的下一个节点中间
            xp.next = x;// p 的下一个节点指向 初始化的x
            // x 节点的上一节点指向p ,x 的父节点指向p
            x.parent = x.prev = xp;
            if (xpn != null)// 之前p 的下一节点
                ((TreeNode<K,V>)xpn).prev = x; // 将之前p 的下一节点的前节点指向刚插入的x节点
             // 重平衡红黑树 并保证root 节点为首节点
            moveRootToFront(tab, balanceInsertion(root, x));
            return null;// 插入成功返回null
        }
    }
}

2.3 获取元素get(“key”):

public V get(Object key) {
    Node<K,V> e;// 声明节点e
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}
 /**
* Implements Map.get and related methods
 *
 * @param hash hash for key
 * @param key the key
 * @return the node, or null if none
 */
final Node<K,V> getNode(int hash, Object key) {
	// 声明tab 数组,声明首节点first,声明 节点e;声明n,k
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    // table 数组赋值给tab ,数组不为空并且数组的长度大于0,
    //  并且通过key 的hash 对应的table 的下标元素不为空
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        // 如果找到的数组原始的hash 和 key 的hash 相同
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {// 取得下个节点
        	// 下个节点是 tree 结构,则从tree 中找到对应key的节点
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {//  从链表找到对应key 的节点
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}
final TreeNode<K,V> getTreeNode(int h, Object k) {
	// 从根节点进行遍历查找对应key 的节点		
    return ((parent != null) ? root() : this).find(h, k, null);
}
 final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
 	// 将根节点赋值给p
   TreeNode<K,V> p = this;
    do {
    	// 声明 ph ,dir,pk 
        int ph, dir; K pk;
        // 将p 的左子节点赋值给pl;右子节点赋值给pr,声明q 节点
        TreeNode<K,V> pl = p.left, pr = p.right, q;
        // 如果要查找的key 对应的hash 小于p 节点的hash
        if ((ph = p.hash) > h)
            p = pl;// 证明要查找的节点在左子树
        else if (ph < h)// 如果要查找的key 对应的hash 大于p 节点的hash
            p = pr;// 证明要查找的节点在右子树
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;//如果要查找的key 对应的hash 等于p 节点的hash 则对比key 
            // key 相同则找到节点直接返回p 节点
        else if (pl == null)// 如果p 的左子节点是null
            p = pr;// 将p 指向右子节点
        else if (pr == null)// 如果p 的右子节点是null
            p = pl;// 将p 指向左子节点
        else if ((kc != null ||
                  (kc = comparableClassFor(k)) != null) &&
                 (dir = compareComparables(kc, k, pk)) != 0)
            p = (dir < 0) ? pl : pr;// 在此判定p 执行左子节点或右子节点
        else if ((q = pr.find(h, k, kc)) != null)// 从p 的右子节点寻找
            return q;// 找到直接返回
        else
            p = pl;// 否则将p 的左子节点赋值给p 继续循环
    } while (p != null);
    return null;// 没有找到则返回null
}

2.4 从Map 中移除元素 remove(key):

public V remove(Object key) {
    Node<K,V> e;// 声明节点e
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}
// hash:对应key 的hash 值;key :对应的key,value= null;matchValue= false
// movable= true
final Node<K,V> removeNode(int hash, Object key, Object value,
                           boolean matchValue, boolean movable) {
    // 声明tab 数组,p 节点,n,index
    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) {
        // 如果对应key 的hash 对应的数组位置上有元素
        // 声明node,e 节点,声明k ,v
        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;// 如果改数组的元素就是要找到元素将p 赋值给node
        else if ((e = p.next) != null) {// 改数组下标的元素有后续的元素
            if (p instanceof TreeNode)// 如果p 是红黑树,则从红黑树中获取对应key 的节点
                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);
            }
        }
        // 如果node 不为空,既找到了改节点,则对改节点进行移除
        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)// 如果当前数组中对应下标位置就是node 
                tab[index] = node.next;// 将node 的下一节点放入数组
            else
                p.next = node.next;// 将p 从链表中移除
            ++modCount;
            --size;// map 中元素的个数-1
            afterNodeRemoval(node);
            return node;// 返回node 节点
        }
    }
    return null;
}
// 从红黑树中获取改节点
final TreeNode<K,V> getTreeNode(int h, Object k) {
   return ((parent != null) ? root() : this).find(h, k, null);
}
//  map:hashMap 本身,tab 为数组,movable= true
final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                         boolean movable) {
   int n;// 声明n
   if (tab == null || (n = tab.length) == 0)
       return;// 如果hashMap 没有进行初始换则直接返回
   // 找到改节点key hash对应tab 数组的下标
   int index = (n - 1) & hash;
   // 将tab 数组中对应位置的元素赋值给first 节点, 
   //声明root 节点将first赋值给root ,
   // 声明rl节点
   TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
   // 将node 节点的下一节点赋值给succ ,
   // 声明pred 节点将node 节点的前置节点进行赋值
   TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
   if (pred == null)
       tab[index] = first = succ;//将node 节点的下一节点赋值给first 节点,
       //并将node 节点下一节点放入到数组中
   else// node 节点不为root 跟节点
       pred.next = succ;// 将node 节点的前置节点的下一节点指向node 的下一节点
   if (succ != null)// node 的下一节点不为空
       succ.prev = pred;// 将node 下一节点的前置节点指向node 的上一节点
    // 此时已经将node 节点从双向链表中进行了移除
   if (first == null)// 如果root 节点是空的则直接返回
       return;
      // 如果root 节点的parent 不为空则说明改root 节点不为根节点
   if (root.parent != null)
       root = root.root();// 找到红黑树的根节点并赋值给root
   // 如果root节点为null 或者root 没有右子节点,
   // 或者root 没有左子节点,或者root 的左子节点
   // 的左子节点为null 红黑树大小,则将红黑树转换成成链表结构
   if (root == null || root.right == null ||
       (rl = root.left) == null || rl.left == null) {
       // 将移除了改node 节点之后的红黑树next 遍历 
       // 组合成单向链表并返回链表的首节点
       tab[index] = first.untreeify(map);  // too small
       return;// 完成转换直接返回
   }
   // 声明p 节点为当前要移除红黑树的node节点,pl 为当前节点的左子节点,
   // pr为当前节点的右子节点
   // 声明replacement 节点
   TreeNode<K,V> p = this, pl = left, pr = right, replacement;
   // 如果改节点即有左节点又有右节点,需要找到可以上位 顶贴要删除节点的位置
   if (pl != null && pr != null) {
   		// 将要移除的node 节点的右子节点赋值给s ,声明sl 节点
       TreeNode<K,V> s = pr, sl;
       //  将node 节点的右子节点的左子节点 赋值给sl ,
       // 如果sl 不为null ,即node 节点的右子树
       // 同样拥有左节点
       while ((sl = s.left) != null) // find successor
           s = sl;// 继续将sl 节点赋值s 节点
        //  上面while 循环完成后,s,sl 指向node 右子树,最深的左节点
       // 将改s 节点的颜色赋值给c,将p 节点的颜色赋值给节点颜色,
       // 将p节点严重赋值为s 节点颜色
       boolean c = s.red; s.red = p.red; p.red = c; // swap colors
       // 声明sr 节点为s 的右子节点
       TreeNode<K,V> sr = s.right;
       // 声明pp 节点为 p 的父节点
       TreeNode<K,V> pp = p.parent;
       // p 的右子节点的左子节点为空,即p 的右子节点只有右子树
       if (s == pr) { // p was s's direct parent 
           p.parent = s;// 将p 的父节点指向 s节点 
           s.right = p;// s 的右子节点指向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 的左子节点置空
       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;// 左子节点赋值给replacement 节点
   else if (pr != null)// 如果只有右子节点
       replacement = pr;// 右子节点赋值给replacement 节点
   else
       replacement = p;// 改节点的左右节点都为空,将当前节点赋值给replacement 节点
   if (replacement != p) {// replacement 不是p 节点
   	   // 将replacement节点的父节点指向 p 节点的父节点,并将p 节点的父几点赋值pp
       TreeNode<K,V> pp = replacement.parent = p.parent;
       if (pp == null)// pp 节点为null 则原有p 节点为root 节点
           root = replacement;// 将root 节点执行替换的节点
       else if (p == pp.left)// p 节点不为root 节点,如果p 节点是p 父节点的左节点
           pp.left = replacement;// pp 的左节点指向要替换的节点
       else// p 节点是pp 的右子节点
           pp.right = replacement;// pp 的右节点指向要替换的节点
       // 将p 节点从树中移除
       p.left = p.right = p.parent = null;
   }
	// 如果p 节点是红色 直接返回root 否则执行 balanceDeletion
	// 即如果移除的节点是红色 则不影响红黑树的定义,否则需要重新为红黑树进行调整
   TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
   
   if (replacement == p) {  // detach
       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);// 确保root 节点为首节点
}
// 获取红黑树的根节点
final TreeNode<K,V> root() {
    for (TreeNode<K,V> r = this, p;;) {
        if ((p = r.parent) == null)
            return r;
        r = p;
    }
}
final Node<K,V> untreeify(HashMap<K,V> map) {
    Node<K,V> hd = null, tl = null;
    for (Node<K,V> q = this; q != null; q = q.next) {
        Node<K,V> p = map.replacementNode(q, null);
        if (tl == null)
            hd = p;
        else
            tl.next = p;
        tl = p;
    }
    return hd;
}

2.4 遍历HashMap:

Map<String, Object> map = new HashMap(32);
Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<String, Object> entry = it.next();
    System.out.println("key:" + entry.getKey() + " "
            + "Value:" + entry.getValue());
}

entrySet().iterator():
调用EntrySet 的 iterator()

public final Iterator<Map.Entry<K,V>> iterator() {
    return new EntryIterator();
}
final class EntryIterator extends HashIterator
    implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}
// 调用HashIterator 的构造方法:
HashIterator() {
    expectedModCount = modCount;
    Node<K,V>[] t = table;
    current = next = null;
    index = 0;// 初始化下标
    // next 赋值 初始化时 next = t[0]
    if (t != null && size > 0) { // advance to first entry
        do {} while (index < t.length && (next = t[index++]) == null);
    }
}
// HashIterator nextNode
final Node<K,V> nextNode() {
    Node<K,V>[] t;
    Node<K,V> e = next;// 元素赋值
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    if (e == null)
        throw new NoSuchElementException();
    // next 下一元素赋值
    if ((next = (current = e).next) == null && (t = table) != null) {
        do {} while (index < t.length && (next = t[index++]) == null);
    }
    return e;
}
// 遍历时元素判断
public final boolean hasNext() {
    return next != null;
}
// 遍历时下一元素获取 it.next()
final class EntryIterator extends HashIterator
    implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}

3 总结:
3.1 HashMap 的数据结构使用的是数组+单向链表+红黑树(红黑树结构中包包含了双向链表)的数据结构,HashMap 数据结构是在put 元素的时候进行的初始化;
3.2 在向HashMap 存放元素时,如果发生hash 碰撞会先在对应碰撞元素的数组位置形成单向链表,如果单向链表的长度大于7并且数组的长度大于64 则触发单向链表转换为红黑树;
3.3 在HashMap 中数组的长度达到阈值时回触发resize() ,先将现有的数组容量扩大一倍,然后将原有的数据向新的数组中转移;转移过程中,如果发现之前时红黑树的节点在转移时元素数量小于等于6则会触发红黑树转链表结构;
3.4在对HashMap 进行get 时,会先找到对应数组下标位置的元素进行比对,如果key 相同则返回,否则判断改数组下标位置是红黑树则进入红黑树从根节点进行查找;是链表则从链表的首节点依次向后查找;
3.5 在对HashMap 进行remove时,会先找到对应数组下标位置的元素进行比对,如果该位置只有一个元素且key 是相同的则直接进行移除;如果发现改节点右其他元素,改节点是红黑树,则将改节点从红黑树中进行移除,移除后如果发现红黑树过小则触发红黑树向链表结构转变;如果是链表则从链表中移除改元素;

4 参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值