HashMap

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GrZxJTzR-1691832408829)(java_source_imgs\1.png)]

HashMap

  1. key 允许null value 也可以为 Null
  2. HashTable 也是实现了map接口
  3. HashMap 和 HashTable 类似,只是HashTable 时同步的,并且不允许null (that it is unsynchronized and permits nulls)
  4. 不能保证映射的顺序
  5. hashMap 和初始化容量和load factor 相关,如果,hashMap中的元素超过了初始化容量和load factor,就会重新分配,并且容量为以前的两倍
  6. 取模最好的公式: hash % n = hash & (n - 1) -> 但 n 必须是 2 的次幂,看看为啥
  7. hashmap 是数组 + 链表 + 红黑树构成,尽管 hashmap key 和 value 都可以为null,但并不是 node 数组为 null,而是 node 的 key 和 value 属性为 null
  8. containsValue 为啥没关注树结构

tableSizeFor

根据入参,返回大于入参&2的次方(但是不能大于 1<<30)
n |= n >>> 1 表示 n 的后一位变成 1 1000000 | 0100000 = 1100000
n |= n >>> 2; 表示 n 的第3、4位变成1 1100000 | 0011000 = 1111000
n |= n >>> 4; 表示 n 的第5~8位变成 1 1111000 | 0000111 = 1111111
如果 n 小于 0 ,则返回 1,另外需要判断是否大于 1 << 30,
n + 1 就是要保证是 2 的次方

/**
 * 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;
}

getNode


/**
  * 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) {
     Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
     // table 不为空 & table.size 不为空 & tab[(n - 1) & hash] != null(链表不为空),执行内容,否则直接返回 null
     if ((tab = table) != null && (n = tab.length) > 0 &&
         (first = tab[(n - 1) & hash]) != null) {
         // 是否第一个元素
         if (first.hash == hash && // always check first node
             ((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;
 }

containsKey

判断是否包含当前key,应该用 containsKey ,而不是通过 get(key) == null 进行判断,HashTable 可以

/**
* Returns <tt>true</tt> if this map contains a mapping for the
 * specified key.
 *
 * @param   key   The key whose presence in this map is to be tested
 * @return <tt>true</tt> if this map contains a mapping for the specified
 * key.
 */
public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

containsValue

为啥没关注树

/**
 * Returns <tt>true</tt> if this map maps one or more keys to the
 * specified value.
 *
 * @param value value whose presence in this map is to be tested
 * @return <tt>true</tt> if this map maps one or more keys to the
 *         specified value
 */
public boolean containsValue(Object value) {
    Node<K,V>[] tab; V v;
    if ((tab = table) != null && size > 0) {
        for (int i = 0; i < tab.length; ++i) {
            for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                if ((v = e.value) == value ||
                    (value != null && value.equals(v)))
                    return true;
            }
        }
    }
    return false;
}

clear

 /**
  * Removes all of the mappings from this map.
  * The map will be empty after this call returns.
  */
 public void clear() {
     Node<K,V>[] tab;
     modCount++;
     if ((tab = table) != null && size > 0) {
         size = 0;
         for (int i = 0; i < tab.length; ++i)
             tab[i] = null;
     }
 }

构造函数

​ putMapEntries 这个函数比较有意思,

		1.  float ft = ((float)s / loadFactor) + 1.0F; ==为啥==
		2.  int s = m.size();        这儿是要获取原先map的元素个数,并不是原先元素的容量。====

/**
     * Constructs a new <tt>HashMap</tt> with the same mappings as the
     * specified <tt>Map</tt>.  The <tt>HashMap</tt> is created with
     * default load factor (0.75) and an initial capacity sufficient to
     * hold the mappings in the specified <tt>Map</tt>.
     *
     * @param   m the map whose mappings are to be placed in this map
     * @throws  NullPointerException if the specified map is null
     */
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }
/**
     * Implements Map.putAll and Map constructor.
     *
     * @param m the map
     * @param evict false when initially constructing this map, else
     * true (relayed to method afterNodeInsertion).
     */
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

红黑二叉树

特性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z8hYEDDM-1691832408830)(java_source_imgs/red_black_tree_features.jpg)]

红黑二叉树旋转小技巧

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2lg4a1II-1691832408831)(java_source_imgs\red_black_1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MxZhj3zF-1691832408832)(java_source_imgs\red_black_2.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XCGlRccC-1691832408833)(java_source_imgs\red_black_3.png)]

情况 4

当前节点为父结点的右子树,而父结点为祖父节点的左子树(三者不在一条直线上

解决办法:当前节点、父结点、祖父节点不在一条线上,需要先以父结点进行左转,然后再以祖父节点进行右转,并将父结点变成黑色,而祖父节点变成红色。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rcp79Inn-1691832408835)(java_source_imgs\red_black_4.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-txzuBuCY-1691832408836)(java_source_imgs\red_black_5.png)]

情况5

当前节点为父结点的左子树,父结点为祖父节点的左子树(三者在一条直线上

解决办法:首先以父结点进行右转,将父结点设置为黑色节点,而将祖父节点设置为红色节点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G5XPDf2F-1691832408837)(java_source_imgs\red_black_6.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T28o7lPR-1691832408838)(java_source_imgs\red_black_7.png)]

情况6

当前节点为父结点的左子树,父结点为祖父节点的右子树(三者不在一条直线上

解决办法:首先以父结点进行右旋,然后以祖父节点进行左旋,接着将父结点变成黑色,而祖父节点变成红色。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Bm2jxC9-1691832408839)(java_source_imgs/beforechange.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SNQHhCae-1691832408840)(java_source_imgs/change_step.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s4VTQ4v1-1691832408842)(java_source_imgs/change_step2.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DXzzt6XX-1691832408843)(java_source_imgs/change_step3.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2RE7kxXi-1691832408844)(java_source_imgs/change_step4.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B9j27g3Y-1691832408845)(java_source_imgs/change_step5.jpg)]

情况7

当前节点为父结点的右子树,父结点为祖父节点的右子树(三者在同一条直线上

解决办法:直接以父结点进行左旋,并将父结点设置为黑色,祖父节点设置为红色。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1OqCK7WI-1691832408846)(java_source_imgs/right_right_before.png)]

rotateLeft

左转的可能性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ILykvjqe-1691832408847)(java_source_imgs/rotateLeft .png)]

static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                      TreeNode<K,V> p) {
    TreeNode<K,V> r, pp, rl;
    if (p != null && (r = p.right) != null) {
        // r 结点表示 p 结点的右结点
        // p 结点存在,p 结点的右结点存在
        if ((rl = p.right = r.left) != null)
        // 如果 r的左结点存在,要把 左节点放置在  p 的右结点
            rl.parent = p;
        if ((pp = r.parent = p.parent) == null)
        // p.parent 父结点为空, root = r(不知道那种情况)
            (root = r).red = false;
        // pp 表示 p 结点的父结点
        else if (pp.left == p)
        // 如果 p 为 pp 的左孩子
            pp.left = r;
        else
        // 如果 p 为 pp 的右孩子
            pp.right = r;
        // 一定要让 r.left = p; p 的父结点 == r 
        r.left = p;
        p.parent = r;
    }
    
    return root;
}

rotateRight

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IiTQVlwr-1691832408848)(java_source_imgs/rotateRight.png)]

static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                       TreeNode<K,V> p) {
    TreeNode<K,V> l, pp, lr;
    if (p != null && (l = p.left) != null) {
    // 判断 p 和 p 的左结点是否为空
        if ((lr = p.left = l.right) != null)
        // p 的左结点等于 l 的右结点
            lr.parent = p;
        if ((pp = l.parent = p.parent) == null)
        // 如果,父结点为 空, 就将 root == r; 并把 r.red = false
            (root = l).red = false;
      	// pp 表示 p 的父结点
        else if (pp.right == p)
            pp.right = l;
        else
            pp.left = l;
        l.right = p;
        p.parent = l;
    }
    return root;
}

balanceInsertion

static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                            TreeNode<K,V> x) {
    x.red = true;
    for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
        // 1
        if ((xp = x.parent) == null) {
            x.red = false;
            return x;
        }
        // 2
        else if (!xp.red || (xpp = xp.parent) == null)
            return root;
        // 3 
        if (xp == (xppl = xpp.left)) {
            // 3.1
            if ((xppr = xpp.right) != null && xppr.red) {
                xppr.red = false;
                xp.red = false;
                xpp.red = true;
                x = xpp;
            }
            else { // 3.2
                if (x == xp.right) {
                    root = rotateLeft(root, x = xp);
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        root = rotateRight(root, xpp);
                    }
                }
            }
        }
        //4
        else {
            // 4.1
            if (xppl != null && xppl.red) {
                xppl.red = false;
                xp.red = false;
                xpp.red = true;
                x = xpp;
            }
            else { // 4.2
                if (x == xp.left) {
                    root = rotateRight(root, x = xp);
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        root = rotateLeft(root, xpp);
                    }
                }
            }
        }
    }
}
  1. xp == x.parent
  2. xpp == xp.parent
  3. xppl == xpp.left
  4. xppr == xpp.right

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DKGlIHcr-1691832408850)(java_source_imgs\balanceInsertNode.jpg)]

  1. 表示 x 就是根节点,直接插入,然后 修改 red == false

  2. 如果 父节点 不是 红色 或者
    xpp == null ;// 表示 xp 就是根节点,xp 的颜色肯定就是 黑色

  3. xp == xppl == xpp.left; 表示 x 的父节点 xp 是 祖父结点的左子树
    3.1 (xppr = xpp.right) != null && xppr.red 表示 右子树存在,并且是红色。直接 父节点和叔叔结点变黑,祖父结点变红
    ·3.2 表示叔叔结点为空或者为 黑色·

    (这里有一步 x = xp),在旋转之后,x 转到最下面,然后方便 xp == x.parent; xpp = xp.parent

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hk4bCjyV-1691832408851)(java_source_imgs\balanceInsert_right_node.jpg)]

    // 表示当前结点 x 是 xp 的右子树,那么就需要左转
    if (x == xp.right) {
        root = rotateLeft(root, x = xp);
        xpp = (xp = x.parent) == null ? null : xp.parent;
    }
    
    // xp != null; 变成黑色,如果祖父结点不为空, 祖父结点变成红色,并且右转
    if (xp != null) {
        xp.red = false;
        if (xpp != null) {
            xpp.red = true;
            root = rotateRight(root, xpp);
        }
    }
    

split

/**
 * Splits nodes in a tree bin into lower and upper tree bins,
 * or untreeifies if now too small. Called only from resize;
 * see above discussion about split bits and indices.
 *
 * @param map the map
 * @param tab the table for recording bin heads
 * @param index the index of the table being split
 * @param bit the bit of hash to split on
 */
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
    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;
    for (TreeNode<K,V> e = b, next; e != null; e = next) {
        next = (TreeNode<K,V>)e.next;
        e.next = null;
        // e.hash & bit == 0 说明 e.hash < bit,就会连接到 小段
        if ((e.hash & bit) == 0) {
            if ((e.prev = loTail) == null)
                loHead = e;
            else
                loTail.next = e;
            loTail = e;
            ++lc;
        }
        else { // 连接到 大段
            if ((e.prev = hiTail) == null)
                hiHead = e;
            else
                hiTail.next = e;
            hiTail = e;
            ++hc;
        }
    }
	// 如果小段不为空,
    if (loHead != null) {
        if (lc <= UNTREEIFY_THRESHOLD) // 小于 threshold 解数
            tab[index] = loHead.untreeify(map);
        else {
            tab[index] = loHead; // 将小段还放在 tab[index]
            if (hiHead != null) // (else is already treeified) 如果 hiHead != null, 说明
                loHead.treeify(tab);  // 需要重新树化
        }
    }
    if (hiHead != null) {
        if (hc <= UNTREEIFY_THRESHOLD)
            tab[index + bit] = hiHead.untreeify(map);
        else {
            tab[index + bit] = hiHead;
            if (loHead != null)
                hiHead.treeify(tab);
        }
    }
}

treeifyBin

如果 当前结点 个数大于 8个,就可以将链表转换成树,但是 treeifyBin 比较有意思的是,首先查看数组的长度是否小于64; 如果是这样,现在 数组扩容,而不是直接将链表转换成红黑二叉树

。// 然后最后在调用 hd.treeify(tab);

/**
 * Replaces all linked nodes in bin at index for given hash unless
 * table is too small, in which case resizes instead.
 */
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);
    }
}
d = 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);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值