HashSet
HashSet底层存储数据使用数组+链表的方式进行存储
HashSet添加数据的过程
存储数据时,计算HashCode,计算出来的HashCode再通过散列函数计算出其在数组中的索引位置,HashCode类似于地址,两个不相同的元素值计算出来的Hash值一般不同,但是通过散列函数计算之后,不同HashCode可能映射为同一个数组的索引位置,这就需要再通过equals方法来比较两者是否真的相同了,如果不相同,那么以链表的方式也连接到该索引值下,如果相同那么不进行重复存储
如图
在JDK7中相同,链表插入方式为新的在数组中,旧的元素以链表方式链在外面
JDK8及以上版本,旧的元素在数组里面,新的元素以链表方式链在外面 ------七上八下
test
public class SetTest {
public static void main(String[] args) {
Set set = new HashSet();
Employee e1 = new Employee("Tom",18,new MyDate(2002,3,5));
Employee e2 = new Employee("Jerry",19,new MyDate(2001,6,5));
set.add(e1);
set.add(e2);
System.out.println(set);
System.out.println("************test1*************");
e1.setName("Mary");
System.out.println(set);
System.out.println("************test2*************");
set.remove(e1);
System.out.println(set);
System.out.println("************test3*************");
set.add(new Employee("Mary",18,new MyDate(2002,3,5)));
System.out.println(set);
System.out.println("************test4*************");
set.add(new Employee("Tom",18,new MyDate(2002,3,5)));
System.out.println(set);
}
}
结果
结果分析
对于test1,没问题,改变了Set中元素的值,打印Set中的内容也是改变之后的值
对于test2,移除e1,结果看到没有移除掉,这是因为在Set中插入元素时,该元素在数组中存储的位置是通过之前的名为Tom的e1计算的,修改e1的值之后位置并未改变,还是名为Tom的e1位置;remove()方法需要先找到e1,但是通过修改后的e1(名字改为Mary)的hashCode()以及equals()计算之后与之前名为Tom的不一样,所以没找到,所以移除不掉
对于test3,因为Mary占的是Tom的位置,所以再插入一个一样的Mary之后并不会形成hashCode()的冲突,所以插入成功
对于test4,虽然Mary占了Tom的位置,但是不是Tom,所以新插入的Tom经过hashCode()计算之后虽然和Mary相同,但是其equals()与Mary不同,所以以链表的方式放在Mary旁边,插入也成功了
LinkedHashSet
LinkedHashSet是HashSet的子类,它可以按照插入顺序,对Set进行遍历
因为其在向Set内部存储时还加入了两对引用(双向链表)
如下图
因为有这样的结构所以可以进行按照插入顺序的遍历
LinkedHashSet也多使用于需要频繁通过插入顺序遍历的情况