Java Map —— HashMap和TreeMap

1.Map

  • 键不能重复,否则会覆盖原来的值
  • Map使用场景
    词典;统计单词频数;配置项;人员信息等等

2.HashMap

1)默认大小为16,负载因子为0.75,threshold在resize()中赋值为16x0.75 = 12
2)HashMap是在put时分配,具体在resize()中分配
3)索引数组位置

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node[] tab; Node 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 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)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) // -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;
    }
  • 关于hash()
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

  • tableSizeFor(cap)保证为与cap最接近的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 >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

001xxxxxxx
-> 0011xxxxxx
-> 001111xxxx
-> 0011111111
最后再加上1,就是2的幂次方

  • if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))先比较hash,效率更高,hash不等比较直接完毕
  • 树化:MIN_TREEIFY_CAPACITY=64,桶中bin被树化的最小hash表容量,若没有达到,且桶中bin的数量达到8,则resize()扩容
 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
     treeifyBin(tab, hash);
  • 扩容resize()
    16 - 0000 1111
    32 - 0001 1111
    根据e.hash & oldCap是否为0将槽拆分为两个表,为0放到loHead,为1放到hiHead,插入新槽的j和j+oldCap位置:
    else { // preserve order
        Node loHead = null, loTail = null;
        Node hiHead = null, hiTail = null;
        Node 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;
        }
    }
  • get()
    返回值为null有两种情况:a)值为null;b)不存在该key
    需要通过containsKey()方法来进行区别
    HashMap的键和值均可为null.
  • HashMap多线程对象丢失与死链分析
    对象丢失:并发put时,竞争覆盖;扩容resize-transfer,新增元素放在已遍历区间,或者并发扩容时新表被覆盖
    死链:逆序插入。扩容时,线程1获取了链表的视图(原来的顺序),线程2完成了扩容将链表逆序插入(新的顺序与原来的顺序相反),thread1又重新插入导致循环死链
  • JDK8与JDK7的改变
    a)插入链表尾部
    b)增加了红黑树
    c)扩容,保证插入有序性;不重新计算hash,使用e.hash&oldCap来判断插入哪个槽

3.TreeMap

  • 插入key必须实现Comparable或提供Comparator,因此key不允许null,但是value可以
  • HashMap使用hashCode/equals实现去重;
    TreeMap依靠Comparable/Comparator实现排序

你可能感兴趣的:(Java Map —— HashMap和TreeMap)