博客名称 |
---|
Java-(中级) |
文章目录
Set-TreeSet集合
java.util.TreeSet集合
TreeSet集合保证元素排序和唯一性的原理
唯一性:是根据比较的返回是否是0来决定。(就是实现了的接口方法的返回值不等于0可以排序,等于0就认为是相同元素)
排序:
A:自然排序(元素具备比较性)
让元素所属的类实现自然排序接口 Comparable
B:比较器排序(集合具备比较性)
让集合的构造方法接收一个比较器接口的子类对象 Comparator
TreeSet集合的特点:排序和唯一
TreeSet集合排序和唯一图解
下面图的大小其实就是自然排序和比较器排序接口中的方法的返回值(这个可看源码)
通过观察TreeSet的add()方法,我们知道最终要看TreeMap的put()方法。
代码演示
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// 自然顺序进行排序
TreeSet<Integer> ts = new TreeSet<Integer>();
// 创建元素并添加
// 20,18,23,22,17,24,19,18,24
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(22);
ts.add(17);
ts.add(24);
ts.add(19);
ts.add(18);
ts.add(24);
//遍历集合
for(Integer i:ts){
System.out.println(i);
}
}
}
结果:
17
18
19
20
22
23
24
TreeSet集合存储自定义对象(实现Comparable接口方式)
需求1:TreeSet存储自定义对象并保证排序和唯一。
代码演示1
测试类
public class TreeSetDemo02 {
public static void main(String[] args) {
//创建TreeSet集合
TreeSet<Student> ts = new TreeSet<>();//默认是自然排序
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
//将学生对象添加到
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
//遍历
for(Student s:ts){
System.out.println(s);
}
}
}
实体类
public class Student {
private String name;
private int age;
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;
}
}
结果:报错了
Exception in thread "main" java.lang.ClassCastException: com.ginger.demo04.Student cannot be cast to java.base/java.lang.Comparable
at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
at java.base/java.util.TreeMap.put(TreeMap.java:536)
at java.base/java.util.TreeSet.add(TreeSet.java:255)
at com.ginger.demo04.TreeSetDemo02.main(TreeSetDemo02.java:25)
TreeSet存储自定义对象并保证排序和唯一报错原因
报错原因:添加到TreeSet集合中的元素在源码中会把该元素向上转型为Comparable,因为Studnet类没有实现Comparable接口,就把Student类向上转型就报了类型转换异常。
还有该需求没有告诉我
A:没有告诉我们怎么排序
自然排序,按照年龄从小到大排序
B:元素什么情况算唯一你也没告诉我
成员变量值都相同即为同一个元素
TreeSet空参构造默认就是使用自然排序,但是有一个前提条件插入该Set集合的元素必须要实现Comparable接口。
代码演示
测试类
public class TreeSetDemo02 {
public static void main(String[] args) {
//创建TreeSet集合
TreeSet<Student> ts = new TreeSet<>();
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
//将学生对象添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
//遍历
for(Student s:ts){
System.out.println(s);
}
}
}
实体类提实现了Comparable演示
想要进行自然排序就一定要实现Comparable接口
public class Student implements Comparable<Student>{
private String name;
private int age;
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 int compareTo(Student o) {
//比较姓名
int num = this.name.compareTo(o.getName());
//姓名相同不代表年龄相同
int num2 = num==0? this.age-o.getAge():num;
return num2;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
结果:
Student{name='fengqingy', age=22}
Student{name='linqingxia', age=27}
Student{name='liushishi', age=22}
Student{name='wanglihong', age=23}
Student{name='wuqilong', age=40}
Student{name='zhangguorong', age=29}
需求2:请按照姓名的长度排序
这里有一个点我记录一下,开始我不是明白按照姓名长度来排序,要怎么去排?不是说实现了Comparable接口就可以自然排序吗?
为什么还要根据姓名长度来排序?
1.首先想要实现自然排序的前提就是,实现Comparable接口,重写compareTo()。
2.TreeSet集合底层是二叉树,首先插入的第一个元素作为根节点,后面插入的数据都要先和根节点做比较小于0向左放,大于0向右放(这里也是看源码了解到的),这个小于0和大于0就是根据compareTo()方法返回值来判断的,这样的话就是为了排序。
举例:首先存储一个叫linqingxia的学生,因为第一个所以作为跟节点
存储第二个学生叫zhangguorong,在compareTo()方法中进行长度比较,linqingxia<zhangguorong通过比较
zhangguorong长度更大,所以就是排在linqingxia后面。
上面我就是随便举了一个小例子,可以看下下面图解
这个需求的难点在于,只说了主要条件,没有说次要条件,次要条件就要自己去分析了。
分析:
//主要条件 请按照姓名的长度排序(已知)
int num = this.name.length()-o.getName().length();
//次要条件 姓名长度相同,不代表姓名内容相同(未知)
int num2 = num0?this.name.compareTo(o.getName()):num;
//次要条件 姓名长度和内容相同,不代表年龄相同(未知)
int num3 = num20?this.age-o.getAge():num2;
代码演示
测试类
public class TreeSetDemo02 {
public static void main(String[] args) {
//创建TreeSet集合
TreeSet<Student> ts = new TreeSet<>();
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
//将学生对象添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
//遍历
for(Student s:ts){
System.out.println(s);
}
}
}
实体类
public class Student implements Comparable<Student>{
private String name;
private int age;
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 int compareTo(Student o) {
//主要条件 请按照姓名的长度排序
int num = this.name.length()-o.getName().length();
//次要条件 姓名长度相同,不代表姓名内容相同
int num2 = num==0?this.name.compareTo(o.getName()):num;
//次要条件 姓名长度和内容相同,不代表年龄相同
int num3 = num2==0?this.age-o.getAge():num2;
return num3;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
结果:
Student{name='wuqilong', age=40}
Student{name='fengqingy', age=22}
Student{name='liushishi', age=22}
Student{name='linqingxia', age=27}
Student{name='wanglihong', age=23}
Student{name='zhangguorong', age=29}
TreeSet集合存储自定义对象(实现Comparator接口方式)
需求:请按照姓名的长度排序
代码演示1((实现Comparator接口方式1)自己定义类继承)
测试类
public class TreeSetDemo {
public static void main(String[] args) {
//创建TreeSet集合使用比较器排序
TreeSet<Student> ts = new TreeSet<>(new MyComparator());
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
//将元素添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
//遍历集合
for (Student s:ts){
System.out.println(s);
}
}
}
自定类去继承Comparator
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
//主要条件 姓名的长度排序
int num = s1.getName().length() - s2.getName().length();
//次要条件 姓名长度相同,不代表姓名内容相同
int num1 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
//次要条件 姓名长度相同,以及姓名内容相同,不代表年龄相同
int num2 = num1 == 0 ? s1.getAge() - s2.getAge() : num1;
return num2;
}
}
实体类
public class Student{
private String name;
private int age;
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 +
'}';
}
}
结果:
Student{name='wuqilong', age=40}
Student{name='fengqingy', age=22}
Student{name='liushishi', age=22}
Student{name='linqingxia', age=27}
Student{name='linqingxia', age=29}
Student{name='wanglihong', age=23}
Student{name='zhangguorong', age=29}
代码演示2((实现Comparator接口方式2)匿名内部类)开发中常用
代码演示
public class TreeSetDemo {
public static void main(String[] args) {
//创建TreeSet集合使用比较器排序
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//主要条件 姓名的长度排序
int num = s1.getName().length() - s2.getName().length();
//次要条件 姓名长度相同不代表姓名内容相同
int num1 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
//次要条件 姓名长度和姓名内容相同,不代表年龄相同
int num2 = num1 == 0 ? s1.getAge() - s2.getAge() : num1;
return num2;
}
});
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
//将元素添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
for (Student s : ts) {
System.out.println(s);
}
}
}
实体类
public class Student{
private String name;
private int age;
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 +
'}';
}
}
结果:
Student{name='wuqilong', age=40}
Student{name='fengqingy', age=22}
Student{name='liushishi', age=22}
Student{name='linqingxia', age=27}
Student{name='linqingxia', age=29}
Student{name='wanglihong', age=23}
Student{name='zhangguorong', age=29}
键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
自然排序(默认从小到大)
比较器排序(默认从小到大)
但是题目要求从大到小怎么办
只要将主要条件的对象对调一下就可以了
排序从小到大
//主要条件 总分从高到低输出到控制台
int num = (s1.getChinese() + s1.getMath() + s1.getEnglish()) - (s2.getChinese() + s2.getMath() + s2.getEnglish());
对象对调
排序从大到小
//主要条件 总分从高到低输出到控制台
int num = (s2.getChinese() + s2.getMath() + s2.getEnglish()) - (s1.getChinese() + s1.getMath() + s1.getEnglish());
代码演示
测试类
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//主要条件 总分从高到低输出到控制台
int num = (s1.getChinese() + s1.getMath() + s1.getEnglish()) - (s2.getChinese() + s2.getMath() + s2.getEnglish());
//次要条件 总分相同,不代表语文成绩相同
int num1 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
//次要条件 总分相同,语文成绩相同,不代表数学成绩相同
int num2 = num1 == 0 ? s1.getMath() - s2.getMath() : num1;
//次要条件 总分相同,语文成绩相同,不代表姓名相同
int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
return num3;
}
});
System.out.println("录入学生信息开始");
for (int x = 1; x <= 5; x++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第" + x + "学生姓名");
String name = sc.nextLine();
System.out.println("请输入第" + x + "学生语文成绩");
String chinese = sc.nextLine();
System.out.println("请输入第" + x + "学生数学成绩");
String math = sc.nextLine();
System.out.println("请输入第" + x + "学生英语成绩");
String english = sc.nextLine();
//创建学生对象
Student s = new Student();
s.setName(name);
s.setChinese(Integer.parseInt(chinese));
s.setMath(Integer.parseInt(math));
s.setEnglish(Integer.parseInt(english));
//将学生对象添加到集合
ts.add(s);
}
System.out.println("学生信息录入完毕");
System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");
for (Student s : ts) {
System.out.println(s.getName()+"\t"+s.getChinese()+"\t"+s.getMath()+"\t"+s.getEnglish());
}
}
}
实体类
public class Student {
// 姓名
private String name;
// 语文成绩
private int chinese;
// 数学成绩
private int math;
// 英语成绩
private int english;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getEnglish() {
return english;
}
public void setEnglish(int english) {
this.english = english;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", chinese=" + chinese +
", math=" + math +
", english=" + english +
'}';
}
}
学生信息录入完毕
姓名 语文成绩 数学成绩 英语成绩
小明 100 50 40
小丽 90 60 80
小曼 100 79 66
小方 100 99 77
小米 100 100 100