6.1 基本概念
- java.util.Set集合是Collection集合的子集合,与List集合平级。该集合中元素没有先后放入次序,且不允许重复。
- 该集合的主要实现类是:HashSet类 和 TreeSet类以及LinkedHashSet类。
6.2 HashSet类
6.2.1 HashSet类特点
- HashSet的底层是采用哈希表进行数据管理的,添加的元素,是无序,不重复,无索引的。
1)代码示例
package cn.guardwhy_05;
import java.util.HashSet;
import java.util.Set;
/**
HashSet集合:
Collection集合的体系:
Collection<E>(接口)
/ \
Set<E>(接口) List<E>(接口)
/ / \ \
HashSet<E>(实现类) LinkedList<E>(实现类) Vector(线程安全) ArrayList<E>(实现类)
/
LinkedHashSet<E>(实现类)
Collection集合体系的特点:
Set系列集合: 添加的元素,是无序,不重复,无索引的。
-- HashSet:添加的元素,是无序,不重复,无索引的。
-- LinkedHashSet:添加的元素,是有序,不重复,无索引的。
*/
public class HashSetDemo01 {
public static void main(String[] args) {
// 无序不重复无索引的
Set<String> sets = new HashSet<>();
// 往集合中添加元素
sets.add("Java");
sets.add("Java");
sets.add("Mybatis");
sets.add("MySQL");
sets.add("MySQL");
sets.add("Spring");
System.out.println("set集合:" + sets); // set集合:[Java, MySQL, Spring, Mybatis]
}
}
6.2.1 元素去重复
1) 代码示例
package cn.guardwhy_05;
import java.util.HashSet;
import java.util.Set;
/**
Set系列集合元素去重复的流程:
1.对于基本数据类型的值,Set集合可以直接判断进行去重复。
2.对于引用数据类型的值,Set集合是按照如下流程进行是否重复的判断。
Set集合会让两两对象,先调用自己的hashCode()方法得到彼此的哈希值(所谓的内存地址)
然后比较两个对象的哈希值是否相同,如果不相同直接认为两个对象不重复。
如果相同,会继续让两个对象进行equals比较内容是否相同,如果相同认为真的重复了,如果不相同认为不重复。
调用hashCode()方法获取两个对象的哈希值
/ \
false true
/ \
不重复 两个对象进行equals比较
/ \
false true
/ \
不重复 重复了
总结:
对于引用数据类型,如果希望Set集合识别是同一个对象。可以重写对象的hashCode和equals方法。
*/
public class HashSetDemo02 {
public static void main(String[] args) {
// 无序不重复无索引的
Set<Student> sets = new HashSet<>();
// 创建对象
Student s1 = new Student("curry", 10 ,'男');
Student s2 = new Student("kobe", 41 ,'男');
Student s3 = new Student("james", 35 ,'男');
Student s4 = new Student("kobe", 41 ,'男');
// 添加操作
sets.add(s1);
sets.add(s2);
sets.add(s3);
sets.add(s4);
System.out.println("HashSet集合:" + sets);
}
}
6.2.2 集合元素无序
1) 代码示例
package cn.guardwhy_05;
import java.util.HashSet;
import java.util.Set;
/**
Set系列集合元素无序的根本原因:
Set系列集合添加元素无序的根本原因是因为底层采用了哈希表存储元素。
JDK 1.8之前:哈希表 = 数组 + 链表 + (哈希算法)
JDK 1.8之后:哈希表 = 数组 + 链表 + 红黑树 + (哈希算法)
总结:
Set集合的增删改查性能都挺好的!
缺点是:无序不重复的,无索引的。
*/
public class HashSetDemo03 {
public static void main(String[] args) {
Set<String> sets1 = new HashSet<>();
// 往集合中添加元素
sets1.add("Java");
sets1.add("Python");
sets1.add("Mybatis");
sets1.add("MySQL");
sets1.add("CSS");
sets1.add("Java");
System.out.println("set集合:" + sets1); // set集合:[Java, CSS, MySQL, Mybatis, Python]
}
}
6.3 LinkedHashSet类
6.3.1 LinkedHashSet特点
- 元素是有序不重复,无索引。底层依然是使用哈希表存储元素的,但是每个元素都额外带一个链来维护添加顺序。
- 元素不光增删改查快,还有序。
6.3.2 区别
LinkedHashSet类与HashSet类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。
1)代码示例
package cn.guardwhy_05;
import java.util.LinkedHashSet;
import java.util.Set;
/**
总结:
如果希望元素可以重复,又有索引,查询要快用ArrayList集合。
如果希望元素可以重复,又有索引,增删要快询要快用LinkedList集合。
如果希望增删改查都很快,但是元素不重复以及无序无索引,那么用HashSet集合。
如果希望增删改查都很快且有序,但是元素不重复以及无索引,那么用LinkedHashSet集合。
*/
public class LinkedHashSetDemo04 {
public static void main(String[] args) {
// 创建sets集合对象
Set<String> sets = new LinkedHashSet<>();
// 添加元素
sets.add("MyBatis");
sets.add("Java");
sets.add("MySQL");
sets.add("Spring");
sets.add("Java");
// 输出集合元素
System.out.println("LinkedHashSet集合:" + sets); // LinkedHashSet集合:[MyBatis, Java, MySQL, Spring]
}
}
6.4 TreeSet类
6.4.1 TreeSet类特点
- TreeSet类的底层是采用红黑树进行数据管理的,因此元素有大小次序,默认从小到大打印。
- 新元素插入到TreeSet集合时,需要使用新元素与集合中已有的元素依次比较来确定新元素的合理位置。
1)代码示例
package cn.guardwhy_07;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest01 {
public static void main(String[] args) {
// 1.准备一个TreeSet集合并打印
Set<String> s1 = new TreeSet<>();
System.out.println("s1 = " + s1); // [啥也没有]
// 2.向集合中添加String类型的对象并打印
boolean b1 = s1.add("aa");
System.out.println("b1 = " + b1); // true
System.out.println("s1 = " + s1); // [aa]
b1 = s1.add("cc");
System.out.println("b1 = " + b1); // true
System.out.println("s1 = " + s1); // [aa, cc]
b1 = s1.add("bb");
System.out.println("b1 = " + b1); // true
// 由于TreeSet集合的底层是采用红黑树实现的,因此元素有大小次序,默认从小到大打印
System.out.println("s1 = " + s1); // [aa, bb, cc]
}
}
6.4.2 自然排序
使用元素的自然排序规则进行比较并排序,让元素类型实现java.lang.Comparable接口
1)代码示例
package cn.guardwhy_07;
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*
* 自然排序
*/
@Override
public int compareTo(Student o) {
/*
// 1.姓名比较
int compare = this.getName().compareTo(o.getName());
if(0 == compare){
// 2.年龄比较
return this.getAge() - o.getAge();
}
// 3.返回比较器
return compare;
*/
// 2.简化写法
// 2.1 姓名比较
int compare = this.getName().compareTo(o.getName());
// 2.年龄比较
return 0 != compare ? compare : this.getAge() - o.getAge();
}
}
2) 代码示例
package cn.guardwhy_07;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest02 {
public static void main(String[] args) {
// 1.准备一个TreeSet集合并放入Student类型的对象
Set<Student> s1 = new TreeSet<>();
// 2.添加到集合
s1.add(new Student("kobe", 41));
s1.add(new Student("curry", 10));
s1.add(new Student("James", 36));
s1.add(new Student("kobe", 38));
// 3.输出结果
System.out.println("s1=" + s1);
// s1=[Student{name='James', age=36}, Student{name='curry', age=10}, Student{name='kobe', age=38}, Student{name='kobe', age=41}]
}
}
6.4.3 比较器排序
- 使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口。
- 自然排序的规则比较单一,而比较器的规则比较多元化,而且比较器优先于自然排序。
1)代码示例
package cn.guardwhy_07;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2) 代码示例
package cn.guardwhy_07;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest03 {
public static void main(String[] args) {
// 4. 匿名比较器对象作为参数传递给构造方法
/*
Comparator<Student> comparator = new Comparator<Student>() {
@Override
// 3.1 准备一个比较器对象作为参数传递给构造方法
public int compare(Student o1, Student o2) {
// 3.2 表示按照年龄比较
return o1.getAge() - o2.getAge();
}
};
*/
// 5.从Jdk1.8支持Lambda表达式: (参数列表) -> { 方法体 }
Comparator<Student> comparator = (Student o1, Student o2)->{
return o1.getAge() - o2.getAge();
};
// 1.准备一个TreeSet集合并放入Student类型的对象
Set<Student> s1 = new TreeSet<>(comparator);
// 2.添加到集合
s1.add(new Student("kobe", 41));
s1.add(new Student("curry", 10));
s1.add(new Student("James", 36));
s1.add(new Student("kobe", 38));
// 3.输出结果
System.out.println("s1=" + s1);
// s1=[Student{name='curry', age=10}, Student{name='James', age=36}, Student{name='kobe', age=38}, Student{name='kobe', age=41}]
}
}