Java 集合(三) Set
Set 系列集合的底层就是 Map
实现的,只是 Set 集合中的元素只要键数据(因为键是唯一的),不要值数据而已。
Set 集合对比
无序:存取顺序不一致
不重复:可以去除重复
无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
- HashSet : 无序、不重复、无索引。
- LinkedHashSet:有序、不重复、无索引。
- TreeSet:排序、不重复、无索引。
HashSet
HashSet 底层采用 哈希表
存储数据,哈希表是一种对于 增删改查数据性能都比较好
的结构。
HashSet 集合判断两个元素相等的标准是两个对象通过 equals()
方法比较相等,并且两个对象的 hashCode()
方法返回值也相等。
public class UseHashSet {
public static void main(String[] args) {
// 使用多态的方法创建一个 HashSet 对象
Set<String> set = new HashSet<>();
set.add("Simon");
set.add("Alex");
set.add("Robbie");
// 重复数据将不会被保存
set.add("Robbie");
System.out.println(set);
}
}
输出结果:
[Alex, Simon, Robbie]
由输出结果可以看出,其在集合中存储的位置是无序的(即:不是按照输入顺序来保存的)。
哈希表
JDK8之前的,底层使用 数组+链表
组成。
JDK8开始后,底层采用 数组+ 链表+红黑树
组成。
均使用 链地址法
解决冲突。
LinkedHashSet
LinkedHashSet 是 HashSet 的子类,其原理是在 HashSet 的基础之上为每个元素添加了一个 前趋指针和后继指针
以便于顺序输出元素。
当遍历 LinkedHashSet 集合里的元素时,LinkedHashSet 会 按元素的添加顺序
来访问集合里的元素。但是由于要维护元素的插入顺序,在 性能上略低与HashSet
,但在迭代访问 Set 里的全部元素时有很好的性能。
public class UseLinkedHashSet {
public static void main(String[] args) {
// 使用多态的方法创建一个 LinkedHashSet 对象
Set<String> set = new LinkedHashSet<>();
set.add("Simon");
set.add("Alex");
set.add("Robbie");
// 重复数据将不会被保存
set.add("Robbie");
System.out.println(set);
}
}
输出结果为:
[Simon, Alex, Robbie]
由输出结果可以看出,LinkedHashSet 的输出顺序是有序的。
TreeSet
TreeSet 的特点是所保存的元素是 按照一定规则进行排序
的。
TreeSet 的底层基于 红黑树
实现,其 增删改查性能都很好
。
TreeSet 的排序规则
① TreeSet 默认排序规则为:
- 对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
- 对于字符串类型:默认按照首字符的编号升序排序。
- 对于自定义类型如 Student 对象,TreeSet 无法直接排序。(想要使用 TreeSet 存储自定义类型,需要制定排序规则)
② 自定义排序规则
- 方法一:让自定义的类(如学生类)实现
Comparable
接口重写里面的compareTo
方法来定制比较规则。 - 方法二:TreeSet 集合有
参数构造器
,可以设置 Comparator 接口对应的比较器对象,来定制比较规则。
案例1:TreeSet 的默认排序规则
public class UseTreeSetByDefault {
public static void main(String[] args) {
// 创建两个 TreeSet 对象
Set<Double> doubleSet = new TreeSet<>();
Set<String> stringSet = new TreeSet<>();
// 为 TreeSet 对象赋值,Double 类型按照升序排序,String 类型按照 ASCII 码排序
Collections.addAll(doubleSet, 12.3, 71.2, 64.5, 33.2);
Collections.addAll(stringSet, "Simon", "Alex","alex" ,"Robbie");
// 输出集合
System.out.println(doubleSet);
System.out.println(stringSet);
}
}
输出结果为:
[12.3, 33.2, 64.5, 71.2]
[Alex, Robbie, Simon, alex]
案例2:TreeSet 的自定义排序之在类中重写 compareTo
创建一个学生类,实现 Compareable
接口,并改写 compareTo
方法。
// 创建一个学生类,实现 Compareable 接口
public class Student implements Comparable<Student>{
private String name;
private int age;
// 改写 compareTo 方法
@Override
public int compareTo(Student stu) {
// 根据年龄升序排序
return this.getAge() - stu.getAge();
}
// 有参构造器
public Student(String name, int age) {
= name;
this.age = age;
}
// 无参构造器
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 重写 toString 方法以便格式化输出
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
创建一个 Student 类的 TreeSet 对象
public class UseTreeSetByCompareable {
public static void main(String[] args) {
// 创建一个 TreeSet 对象
Set<Student> studentSet = new TreeSet<>();
// 为集合添加 Student 类型的元素(通过有参构造器接受)
studentSet.add(new Student("Simon", 21));
studentSet.add(new Student("Robbie", 23));
studentSet.add(new Student("Alex", 22));
System.out.println(studentSet);
}
}
输出结果:
[Student{name=‘Simon’, age=21}, Student{name=‘Alex’, age=22}, Student{name=‘Robbie’, age=23}]
由于改写了 compareTo 的规则,输出按照年龄升序输出。
案例3:TreeSet 的自定义排序之通过参数构造器排序
public class UseTreeSetByConstructor {
public static void main(String[] args) {
// 创建一个 TreeSet 对象,并重新 compare 方法
TreeSet<Student> studentSet = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getAge() - o1.getAge();
}
});
// 为集合添加 Student 类型的元素(通过有参构造器接受)
studentSet.add(new Student("Simon", 21));
studentSet.add(new Student("Robbie", 23));
studentSet.add(new Student("Alex", 22));
System.out.println(studentSet);
}
}
使用 Lambda 表达式书写:
public class UseTreeSetByConstructor {
public static void main(String[] args) {
// 创建一个 TreeSet 对象,并重新 compare 方法
TreeSet<Student> studentSet = new TreeSet<>( (o1, o2) -> (o2.getAge() - o1.getAge()) );
// 为集合添加 Student 类型的元素(通过有参构造器接受)
studentSet.add(new Student("Simon", 21));
studentSet.add(new Student("Robbie", 23));
studentSet.add(new Student("Alex", 22));
System.out.println(studentSet);
}
}
输出结果为:
[Student{name=‘Robbie’, age=23}, Student{name=‘Alex’, age=22}, Student{name=‘Simon’, age=21}]