1 Set集合
Set集合特点
- 无序:存取顺序不一致
- 不重复:可以去除重复
- 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
Set集合实现类特点
- HashSet :无序、不重复、无索引。
- LinkedHashSet:有序、不重复、无索引。
- TreeSet:排序、不重复、无索引。
Set集合的功能上基本上与Collection的API一致
2 HashSet
- HashSet集合底层采取哈希表存储的数据
哈希表是一种对于增删改查数据性能都较好的结构
哈希表的组成
- JDK8之前的,底层使用数组+链表组成
- JDK8开始后,底层采用数组+链表+红黑树组成
- 当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树
- 哈希值:是JDK根据对象的地址,按照某种规则算出来的int类型的数值
- Object类的API:public int hashCode():返回对象的哈希值
- 对象的哈希值特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的
2.1 哈希表详细流程
Step1:创建一个默认长度16,默认加载因子0.75的数组,数组名table
Step2:根据元素的哈希值跟数组的长度计算出应存入的位置
Step3:判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素, 则调用equals方法比较属性值,如果一样,则不存,如果不一样,则存入数组
JDK 7新元素占老元素位置,指向老元素
JDK 8中新元素挂在老元素下面
Step4:当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
Ps:必须重写Object类的equals()和Hashcode()
public class Student { private int age; private String name; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && name.equals(student.name); } @Override public int hashCode() { return Objects.hash(age, name); } }
3 LinkedHashSet
- 有序、不重复、无索引
- 这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序
4 TreeSet
- 可排序、不重复、无索引
- 可排序:按照元素的大小默认升序(有小到大)排序
- TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序
TreeSet集合默认的规则
- 对于数值类型:Integer , Double,官方默认按照大小进行升序排序
- 对于字符串类型:默认按照首字符的编号升序排序
- 对于自定义类型,需要制定排序规则
方式一
- 让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
public class Apple implements Comparable<Apple>{
private int weight;
public Apple() {
}
public Apple(int weight) {
this.weight = weight;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"weight=" + weight +
'}';
}
/**
方式一:类自定义比较规则
o1.compareTo(o2)
* @param o
* @return
*/
@Override
public int compareTo(Apple o) {
// 按照重量进行比较的
return this.weight - o.weight ; // 升序
return o.weight - this.weight ; // 降序
}
}
方式二
- TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。
补充知识点:可变参数
- 可变参数用在形参中可以接收多个数据。
- 可变参数的格式:数据类型…参数名称
public static void main(String[] args) {
sum(); // 1、不传参数
sum(10); // 2、可以传输一个参数
sum(10, 20, 30); // 3、可以传输多个参数
sum(new int[]{10, 20, 30, 40, 50}); // 4、可以传输一个数组
}
/**
注意:一个形参列表中只能有一个可变参数,可变参数必须放在形参列表的最后面
* @param nums
*/
public static void sum( int...nums){
// 注意:可变参数在方法内部其实就是一个数组。 nums
System.out.println("元素个数:" + nums.length);
System.out.println("元素内容:" + Arrays.toString(nums));
}
可变参数作用:
- 接收参数非常灵活,方便。可以不接收参数,可以接收1个或者多个参数,也可以接收一个数组
- 可变参数在方法内部本质上就是一个数组
注意事项:
- 一个形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面