HashMap的链表结构

代码如下

public class HashSet_ {
    public static void main(String[] args) {
        HashSet set = new  HashSet();
        //十二个对象在每一行的头部,使行数达到临界值,从而进行table数组扩容
        for (int i = 0; i <= 12; i++) {
            set.add(i);
        }
        //十二个对象在同一行,使某一行的链表排满,从而进行树化的过程
        for (int i = 0; i <= 12 ; i++) {
            set.add(new A(i));
        }
        System.out.println(set);
    }
}
class A{
    public int a;

    public A(int a) {
        this.a = a;
    }

//重写hashCode方法,让他落在同一链表中,以便实现链表树化
//类中的hashCode方法返回的值可以影响到他在hashMap中的位置
//当hashCode返回值相同的时候,就处在同一链表中(同一行)
    @Override
    public int hashCode() {
        return 100;
    }
}

1、HashMap链表中有三个比较重要的属性,table和threshold(阈值),loadFactor(加载因子)

HashMap的链表结构_第1张图片

2、table属性相当于把每一条链表再连起来的链

通俗一点来说就相当于一张表里的行,每一行都有一串数据,这一串数据就称为链表。tables的引用类型是Node[]对象数组,所以table属性保存第一个传进来的对象,相当于每一行的头部对象都保存在table这个对象数组里面,每一行中的每个对象又挂载到上一个对象的next属性中形成一条链

HashMap的链表结构_第2张图片

从debug里面的属性可以看出,table一开始为空的,调用了add方法新增一个对象后,扩容了16个空间,相当于有了16行,并把这个对象分配给table[0]的位置上(第一行首位)

HashMap的链表结构_第3张图片

相当于 table[0] = Integer 0 ;后续如果有哈希值是和这个对象相同的元素加入,就在这个链表后面的追加。

HashMap的链表结构_第4张图片

3、threshold(阈值):链表的临界值

阈值 = table * loadFactor ,一开始table = null,传入第一个对象后Node[] table = new Node[16]创建一个链表空间,容量为16,可以容纳16条链表,后续添加的对象超过阈值12个之后进行链表空间的扩容,扩容大小为之前的两倍。注意:这12个对象并不一定要分布在每一行,把每一行占满再进行扩容,当某两个链表相加的元素大于12也开始扩容

扩容前:

HashMap的链表结构_第5张图片

 扩容后:HashMap的链表结构_第6张图片

 示意图HashMap的链表结构_第7张图片

HashMap的链表结构_第8张图片

 

 以上为table属性容量不够的情况(行不够用)的扩容方法,那列不够用的情况呢?

1、当链表中的元素等于8时,每新增加一个元素,就先进行数组的扩容,扩容到32,再增加就扩容到64,当增加到64的时候,不在增加,且把链表中的元素树化(红黑树)

链表元素未到8时 ,位置在table[4]

HashMap的链表结构_第9张图片

等于8时,进行数组的扩容table[16]变成table[32]

HashMap的链表结构_第10张图片

 链表元素每增加1,都扩大一倍,当table大小等于64时,table不再扩容,并且该链表也转移了位置

HashMap的链表结构_第11张图片

 当table的容量不再扩容,后续该链表还有元素要加入时,该链表会进行树化,该链表的类型会从Node变成TreeNode,从链表变成红黑树,有左节点和右节点

HashMap的链表结构_第12张图片

HashMap的链表结构_第13张图片

你可能感兴趣的:(链表,java,数据结构)