详说 TreeSet 输出有序的那些事

不知道各位小伙伴有没有疑惑,大家平时用 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,我后期会给大家介绍的
😄😄😄今天暂时就到这里啦。。。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗罗的1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值