Set集合之TreeSet排序详解

在开发的过程中,我们总是会碰上各种各样奇奇怪怪的需求,比如说需要把一个集合或者数组的int数据按照从小到大或者从大到小的顺序排列,这时,我们可能会有很多解决办法,比如说:for循环,把每一个数字都与当前数字进行比较,根据需要看是升序还是降序排列,再交换两个数字的位置,这种方法叫选择排序.
其实在Set中有一个它的子类:TreeSet,它可以实现集合的排序.

关于TreeSet

首先TreeSet是SortedSet的实现类不同于其他Set集合,它采用红黑树的数据结构来存储集合元素,与其他Set集合一样,它也不允许有相同的元素,同时它具有着排序功能.通常情况下,如果我们直接向TreeSet中添加元素时,它是默认使用自然排序来对我们添加的元素进行排序

public static void main(String[] args) {
          // TODO Auto-generated method stub
         Set<Integer> s=new TreeSet<Integer>();
         s.add(1);
         s.add(5);
         s.add(6);
         s.add(8);
         s.add(2);
         s.add(4);
         System.out.println(s.toString());
     }
输出的结果为:1,2,4,5,6,8

默认为从小到大的顺序排列.如果我们要从大到小排列应该怎么办呢?
其实也非常简单,java为我们提供了一个Collection.sort()的方法,我们只需要把需要排序的集合放入这个方法中,并带上一个比较器即可:

public class ListCompareTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("2003");
        list.add("2005");
        list.add("2001");
        list.add("2007");
        System.out.println(list);
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                // 返回相反的compare
                return o2.compareTo(o1);
            }
        });
        //因为是comparator比较器,其实可以用lambda表达式来代替,可以简化成如下形式
        list.sort(Comparator.reverseOrder());
        System.out.println(list);
    }
}

其实如果想要通过这个方法进行升序排列也是可以的,只需要把o2,o1调换一下位置即可.

//下面只给出简化版的代码
list.sort(Comparator.naturalOrder());

我们仔细看一下这个排序的方法,它的核心所在就是compareTo()这个方法,这个方法的作用我们都知道,当第一个参数比第二个参数大的时候,返回值为1;相等时,返回0;小于时,返回-1.可是它明明返回值是int,那为什么能够根据这个方法进行排序呢?
其实很简单,当前对象与后一个对象进行比较,如果比较结果为1进行交换,其他不进行交换。
当后一个对象比当前对象大,返回结果值为-1时,前后交换,说明是倒序排列。
当后一个对象比当前对象小,返回结果值为1时,前后交换,说明是升序排列.
总结一下,也就是说为-1时,就是倒序,为1时,就是正序.
好了上面的是比较简单的一种情况,我们开发中肯定会需要自定义类来封装数据,那如果我们要对自定义的类中的属性值进行排序应该怎么做呢?
比如说我们现在有一个Student类,学生类中只有一个age属性

public class Student{
    private int age;
    Student(int age){
        this.age=age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                '}';
    }
}
public class TestM {
    public static void main(String[] args) {
        TreeSet<Student> objects = new TreeSet<>();
        objects.add(new Student(5));
        objects.add(new Student(5));
        objects.add(new Student(6));
        objects.add(new Student(8));
        System.out.println(objects);
    }
}

这个时候,当我们点击运行的时候,编译器会提示ClassCastException这个异常,为什么呢?因为向TreeSet集合中添加两个及以上的相同对象时,第一个不会出现问题,但第二个,TreeSet就会调用该对象的compareTo()方法与其他元素进行比较,如果对应的类没有实现Comparable接口,就会出现该异常.
那应该怎么办呢?
有两种解决方法,第一种直接在需要的类中实现Comparable接口,重写compareTo()方法,不推荐这样做,因为耦合性会比较高,下面给出Demo:

public class Student implements Comparable{
    private int age;
    Student(int age){
        this.age=age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        Student student =(Student)o;
        //重写compareTo(),传入一个参数,然后拿当前的类的年纪跟传入参数的年纪作为对比,返回-1时,其实就是倒序排列,可以简化为
        //Integer.compare(this.age, student.age);
        return this.age> student.age?-1:this.age< student.age?1:0;
    }
}

这样实现过Comparable接口,重写过compareTo()方法后,就不会出现异常.但这种方法会造成耦合性增加,简单的来说,如果需要按升序排列集合,那就只能去修改实体类的代码,过于麻烦.所以笔者比较推荐使用第二种方法.
第二种方法:直接在new TreeSet时,指定排序.

public class TestM {
    public static void main(String[] args) {
        TreeSet<Student> objects = new TreeSet<>(((o1, o2) ->          o1.age>o2.age?-1:o1.age<o2.age?1:0));//这个地方同样可以使用lambda表达式来代替Integer.compare(o2.age, o1.age)
        objects.add(new Student(5));
        objects.add(new Student(5));
        objects.add(new Student(6));
        objects.add(new Student(8));
        System.out.println(objects);
        }
}

首先这样,我们实体类无需实现Comparable接口,想要的排序也可以自己在业务逻辑的代码中配置,上面代码中为-1时,是倒序排列,为正1即是正序排列.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值