不知道各位小伙伴有没有疑惑,大家平时用 TreeSet 的时候,偶尔输出是有序的,到底怎么回事呢,我们看看下面的例子
public class Test01 {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("a");
treeSet.add("c");
treeSet.add("b");
treeSet.forEach(System.out::println);
}
}
a
b
c
哎哎哎,不是set是无序的吗,这是怎么回事?怎么就变成有序了呢?我们看看String的源码,发现了一个单词 Comparable,意思是可以比较的,难道和这个有关,我不信,换一个来测试
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
试试Integer,它也实现了Comparable接口,看看输出是不是也是有序的
public class Test01 {
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(2);
treeSet.add(4);
treeSet.forEach(System.out::println);
}
}
2
4
5
还真是,那换一个没有实现Comparable的接口来试试,如果无序,就证明和这个接口有关
public class Student{
private int age;
private String name;
private int salary;
public Student(int age, String name, int salary) {
this.age = age;
this.name = name;
this.salary = salary;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", salary=" + salary +
'}';
}
}
测试运行一下,看看输出的是什么
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Student> strings = new TreeSet<>();
Student student = new Student(21, "ljl", 7654);
Student student2 = new Student(21, "lol", 734);
Student student3 = new Student(18, "luo", 2454);
strings.add(student);
strings.add(student2);
strings.add(student3);
Iterator<Student> iterator = strings.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
Exception in thread "main" java.lang.ClassCastException:Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
为社么会出现这种错误呢?我们仔细想想,说 java.util.TreeMap.compare(TreeMap.java:1294) 报错,点进去看看
@SuppressWarnings("unchecked")
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
它在底层帮我们比较,等等,,,,我好像没有写关于如何排序的逻辑啊。。还有啊,假设comparator==null 成立,(Comparable<? super K>)k1,student和Comparable有关系吗,没有吧,那怎么强转呢!是吧,所以报 java.lang.ClassCastException 没有毛病,是吧!
哎,那comparator=null 不成立呢,你怎么不说,我刚才不是说了吗,对于student对象,我说了排序的规则吗,难不成还是人工智能的,知道我想怎么排序。。。。不可能的。还有就是不是已经报了 java.lang.ClassCastException 吗,这说明我们刚才分析的是对的。
那我们来分析分析,上面是怎么指定规则排序的,先来看看 Integer
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
看看String是怎么比较的
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
好,既然我们已经发现了规律,那我们就给students指定我们想要排序的规则,按照String和Integer那样,先实现 Comparable 接口
public class Student implements Comparable<Student>{
private int age;
private String name;
private int salary;
public Student(int age, String name, int salary) {
this.age = age;
this.name = name;
this.salary = salary;
}
setter/getter.....
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", salary=" + salary +
'}';
}
@Override
public int compareTo(Student o) {
return 0;
}
}
发现我们要重写 compareTo 方法,来制定我们的排序规则,我是这样制定的
@Override
public int compareTo(Student o) {
if (this.getAge() == o.getAge()){//年龄相等
return this.getName().compareTo(o.getName());//如果姓名不等就不是同一个人,相等,是同一人,就不会出现在set中
}else if (this.getAge()<o.getAge()){
return -1;
}else {
return 1;
}
}
我们运行试试
Student{age=18, name='luo', salary=2454}
Student{age=21, name='ljl', salary=7654}
Student{age=21, name='lol', salary=734}
对啦,哈哈
总结:平时我们自定义排序的时候,记得要在实体类上实现 Comparable 接口哦,还有一个接口comparator,我后期会给大家介绍的
😄😄😄今天暂时就到这里啦。。。