hashMap1.8之后会有红黑树,树形结构

传统 HashMap的缺点

(1)JDK 1.8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。

(2)当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。

(3)针对这种情况,JDK 1.8 中引入了红黑树(查找时间复杂度为 O(logn))来优化这个问题

TREEIFY_THRESHOLD

一个桶的树化阈值

UNTREEIFY_THRESHOLD

一个树的链表还原阈值

MIN_TREEIFY_CAPACITY

哈希表的最小树形化容量
static final int TREEIFY_THRESHOLD = 8
 static final int UNTREEIFY_THRESHOLD = 6
static final int MIN_TREEIFY_CAPACITY = 64
当桶中元素个数超过这个值时
需要使用红黑树节点替换链表节点
当扩容时,桶中元素个数小于这个值
就会把树形的桶元素 还原(切分)为链表结构
  1. 当哈希表中的容量大于这个值时,表中的桶才能进行树形化
  2. 否则桶内元素太多时会扩容,而不是树形化

如果在创建HashMap实例时没有给定capacity、loadFactor则默认值分别是16和0.75。

当好多bin被映射到同一个桶时,如果这个桶中bin的数量小于TREEIFY_THRESHOLD当然不会转化成树形结构存储;如果这个桶中bin的数量大于了 TREEIFY_THRESHOLD ,但是capacity小于MIN_TREEIFY_CAPACITY 则依然使用链表结构进行存储,此时会对HashMap进行扩容;如果capacity大于了MIN_TREEIFY_CAPACITY ,则会进行树化。


package org.fan.learn.map;

import java.util.regex.Pattern;

/**
 * Created by fan on 2016/4/7.
 */
public class MapKey {

    private static final String REG = "[0-9]+";

    private String key;

    public MapKey(String key) {
        this.key = key;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MapKey mapKey = (MapKey) o;

        return !(key != null ? !key.equals(mapKey.key) : mapKey.key != null);

    }

    @Override
    public int hashCode() {
        if (key == null)
            return 0;
        Pattern pattern = Pattern.compile(REG);
        if (pattern.matcher(key).matches())
            return 1;
        else
            return 2;
    }

    @Override
    public String toString() {
        return key;
    }
}


这个MapKey类用于作为HashMap的key的类型,实现了equals、hashCode、toString方法。其中,hashCode方法故意将所有数字字符串key的hash值返回1,其他字符串key的hash值返回2。


package org.fan.learn.map;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by fan on 2016/4/7.
 */
public class MainTest {
    public static void main(String[] args) {
        Map map = new HashMap();
        /*
        //第一阶段
        for (int i = 0; i < 6; i++) {
            map.put(new MapKey(String.valueOf(i)), "A");
        }
        */

        /*
        //第二阶段
        for (int i = 0; i < 10; i++) {
            map.put(new MapKey(String.valueOf(i)), "A");
        }
        */

        /*
        //第三阶段
        for (int i = 0; i < 50; i++) {
            map.put(new MapKey(String.valueOf(i)), "A");
        }
        */

        /*
        //第四阶段
        map.put(new MapKey("X"), "B");
        map.put(new MapKey("Y"), "B");
        map.put(new MapKey("Z"), "B");
        */
        System.out.println(map);
    }
}

第一阶段

这个时候桶中bin的数量小于TREEIFY_THRESHOLD
Debug如下所示:
这里写图片描述
看一下table中具体的存储方式:
这里写图片描述

第二阶段

这个时候桶中bin的数量大于了TREEIFY_THRESHOLD ,但是capacity不大于MIN_TREEIFY_CAPACITY ,则要扩容,使用链表结构存储。
这里写图片描述
从上图可以看出,1号桶中的存储结构依然是链表。

第三阶段

这个时候桶中bin的数量大于了TREEIFY_THRESHOLD 且 capacity大于了MIN_TREEIFY_CAPACITY ,因此,会树化。
这里写图片描述
对这个输出map的值,可以看到是乱序的,因为是使用树形结构进行存储的。
这里写图片描述

第四阶段

这个阶段主要是测试,如果一个桶采用了树形结构存储,其他桶是不是也采用树形结构存储。结论是,如果其他桶中bin的数量没有超过TREEIFY_THRESHOLD,则用链表存储,如果超过TREEIFY_THRESHOLD ,则用树形存储。

这里写图片描述




你可能感兴趣的:(hashMap1.8之后会有红黑树,树形结构)