[2025.1.11 JavaSE学习]集合-4(Set接口 && HashSet && LinkedHashSet)
Set接口方法
- 无序,添加和取出顺序不一样,无索引
- 没有重复的元素,最多包含一个null
- Set接口也是Collection子接口,常用方法一样
- Set同Collection遍历方式一样:
- 可以使用迭代器(Iterator it = set.iterator())
- 可以使用增强for
- 不能使用索引方式
HashSet
- HashSet实现了Set接口
- HashSet实际上底层为HashMap,其构造方法如下:
public HashSet(){
map = new HashMao<>();
}
- 可以存放null值,但是只能有一个
- HashSet不保证元素是有序的,取决于hash后,再确定索引的结果
- 不能有重复元素
- 底层为数组+链表+红黑树
HashSet扩容机制
-
添加一个元素时,先得到其hash值(hashCode()),会转换为索引值
-
找到table中索引位置是否存放有元素
-
如果没有,直接加入
-
如果有,调用equals()进行比较(底层为地址比较,String内部重写了equals方法,会根据字符串内容进行比较)
-
equals比较后,如果相同,就放弃添加,如果不相同,则添加到最后
-
在jdk8中,如果一个索引链表的元素个数到达TREE_THRESHOLD(默认为8)且table使用大小大于等于MIN_TREEIFY_CAPACITY(默认64),就会红黑树化
- 每次将元素添加在链表后,立刻判断是否已经达到8个节点,如果是,则调用treeifyBin()
- 在转为红黑树之前,需要进行判断条件:
if(tab == null || (n = tab.length) < (MIN_TREEIFY_CAPACITY(64))) resize(); - 如果上面条件成立,先对table进行扩容;否则进行红黑树化
-
一些细节:
- 第一次添加时,table数组就扩容到16,临界值threshold为16loadFactor(160.75=12)
- 如果table数组使用达到了临界值12,就会扩容到16*2=32,新的临界值就是32*0.75=24,以此类推
- 若达到上述条件,则红黑树化
- PS:table使用大小算的是add()方法的成功次数,即单索引上的链表+1也算在内
- 重写 equals 方法时,必须同时重写 hashCode 方法:
@Override public boolean equals(Object o){ if(this == o) return ture;//判断是否为自身 if(o == null || getClass() != o.getClass())return false;//判断是否为空,或者不同类 Employee employee = (Employee)o; return age == employee.age && Objects.equals(name, employee.name); } @Override public int hashCode(){//为了根据属性生成相同的hashCode return Objects.hash(name, age); }
LinkedHashSet
- 继承了HashSet
- 底层为一个LinkedHashMap,底层维护了数组+双向链表
- 根据元素的hashCode值决定存储位置,同时使用链表维护元素的次序,使得元素看起来是以插入顺序保存的
- 不允许添加重复元素
- 工作逻辑,按插入顺序建立双向链表,而决定插不插入的逻辑则和HashSet一样
- 源码分析:
- 添加第一次时,直接将数组table扩容到16
- 数组为HashMap$Node(为LinkedHashMap父类,数组多态),存放的元素为LinkedHashMap$Entry类型($是外部类内部类分隔符),Entry为LinkedHashMap内部静态类,继承了HashMap.Node