代码如下
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(加载因子)
2、table属性相当于把每一条链表再连起来的链
通俗一点来说就相当于一张表里的行,每一行都有一串数据,这一串数据就称为链表。tables的引用类型是Node[]对象数组,所以table属性保存第一个传进来的对象,相当于每一行的头部对象都保存在table这个对象数组里面,每一行中的每个对象又挂载到上一个对象的next属性中形成一条链
从debug里面的属性可以看出,table一开始为空的,调用了add方法新增一个对象后,扩容了16个空间,相当于有了16行,并把这个对象分配给table[0]的位置上(第一行首位)
相当于 table[0] = Integer 0 ;后续如果有哈希值是和这个对象相同的元素加入,就在这个链表后面的追加。
3、threshold(阈值):链表的临界值
阈值 = table * loadFactor ,一开始table = null,传入第一个对象后Node[] table = new Node[16]创建一个链表空间,容量为16,可以容纳16条链表,后续添加的对象超过阈值12个之后进行链表空间的扩容,扩容大小为之前的两倍。注意:这12个对象并不一定要分布在每一行,把每一行占满再进行扩容,当某两个链表相加的元素大于12也开始扩容
扩容前:
扩容后:
示意图
以上为table属性容量不够的情况(行不够用)的扩容方法,那列不够用的情况呢?
1、当链表中的元素等于8时,每新增加一个元素,就先进行数组的扩容,扩容到32,再增加就扩容到64,当增加到64的时候,不在增加,且把链表中的元素树化(红黑树)
链表元素未到8时 ,位置在table[4]
等于8时,进行数组的扩容table[16]变成table[32]
链表元素每增加1,都扩大一倍,当table大小等于64时,table不再扩容,并且该链表也转移了位置
当table的容量不再扩容,后续该链表还有元素要加入时,该链表会进行树化,该链表的类型会从Node变成TreeNode,从链表变成红黑树,有左节点和右节点