Java的集合排序:Collections.sort、list.sort和list.stream().sorted方法详解

在工作中我们经常需要对集合中的元素进行比较排序,下面就java中常用的排序方法进行详解。

主要有三种用法:

// 第一种
Collections.sort(studentList);
// 第二种
studentList.sort(Comparator.comparing(Student::getAge).reversed());
// 第三种
List<Student> studentList = studentList.stream().sorted(Comparator .comparing(Student::getAge)
.reversed()).collect(Collectors.toList());

第一种:

第一种方式的集合中的对象实现了Comparable接口的比较方式,此接口强行对实现它的每个类的对象进行整体排序。此排序被称为该类的自然排序 ,类的 compareTo方法被称为它的自然比较方法 。实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort )进行自动排序。实现此接口的对象可以用作有序映射表中的键或有序集合中的元素,无需指定比较器。这是Collections中的方法,具体实现还是在List接口中,在jdk8中添加的默认方法。

一般使用如下:

Collections.sort(studentList);

或者

Collections.sort(studentList, (o1, o2) -> {
    return o1.getAge() -o2.getAge();
});

实际上第二种方法只不过是多传入了一个比较器,而第一种只不过是把比较器当做默认值null已经传入了。上面是使用lambda表达式的写法,如果是更明确的写法的话:

Collections.sort(studentList, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
});

或者自己实现一个比较器类,然后传入也可以,比如:

public class MyComparator {
     public static int compareTo(Student o1, Student o2){
         int ageComparisonResult = o1.getAge() - o2.getAge();
         int nameComparisonResult = o1.getName().compareTo(o2.getName());
         double heightComparisonResult = o1.getHeight() - o2.getHeight();
         if (ageComparisonResult == 0){
             if (nameComparisonResult == 0){
                 if (heightComparisonResult == 0) {
                     return 0;
                 } else if (heightComparisonResult > 0){
                     return 1;
                 } else {
                     return -1;
                 }
             } else {
                 return nameComparisonResult;
             }
         } else {
             return ageComparisonResult;
         }
     }
}
然后传入sort(List<T> list, Comparator<? super T> c)方法中:
Collections.sort(studentList, (o1, o2) -> MyComparator.compareTo(o1, o2));

该集合中的Student对象必须实现Comparable接口(强制),然后实现它的compareTo方法,否则编译器会报错。

public class Student implements Comparable{     
    private String name;    
    private int age;    
    private double height; 
    
    public Student() {    
    }     
    
    public Student(String name, int age, double height) {        
        this.name = name;        
        this.age = age;        
        this.height = height;    
    } 
    
    // getter and setter
    
    @Override    
    public int compareTo(Object o) {        
        return 0;    
    } 
}

第二种和第三种其实类似,我们放在一起说。

一般使用方法:

// ① 使用reversed方法按照排序规则(age)对元素进行降序排序
studentList.sort(Comparator.comparing(Student::getAge).reversed());

或者

// ② 使用reversed方法按照排序规则(age)对元素进行降序排序
List<Student> studentList = studentList.stream().sorted(Comparator .comparing(Student::getAge)
.reversed()).collect(Collectors.toList());

可以看到不管是sort方法还是sorted方法都需要传入一个比较器,即Comparator,Comparator与上面的Comparable接口不同的是:

  1. Comparator位于包java.util下,而Comparable位于包java.lang下。

  2. Comparable接口将比较代码嵌入需要进行比较的类的自身代码中,而Comparator接口在一个独立的类中实现比较。

  3. 如果前期类的设计没有考虑到类的Compare问题而没有实现Comparable接口,后期可以通过Comparator接口来实现比较算法进行排序,并且为了使用不同的排序标准做准备,比如:升序、降序。

  4. Comparable接口强制进行自然排序,而Comparator接口不强制进行自然排序,可以指定排序顺序。

上面的两种写法最后的方法reversed就是对获取到的属性(age)进行降序排序。

上面的①和②有一定的区别:

  1. ①是List接口再在dk8中添加的默认方法,位于java.util包下;而sorted方法是jdk8新添加的stream接口,位于java.util.stream包下面;

  2. sort()方法如下:

    default void sort(Comparator<? super E> c) {
            Object[] a = this.toArray();
            Arrays.sort(a, (Comparator) c);
            ListIterator<E> i = this.listIterator();
            for (Object e : a) {
                i.next();
                i.set((E) e);
            }
        }

    可以看出,该方法先将list转为了数组,然后调用java.util.Arrays的sort方法,然后使用迭代器的方式将排好序的集合元素放入到原来集合的迭代器序列中。所以sort方法返回值为void,即无返回值。

    它是将原来的集合转化为数组,排序后使用迭代器进行迭代重新放入到原集合的迭代器序列中。所以最终改变的是原集合。

3.stream的sorted方法如下

Stream<T> sorted(Comparator<? super T> comparator);

入参是一个比较器,它最终在ReferencePipeline这个抽象类中做了实现。

4.这两个方法对应的集合中的对象无需实现Comparator接口和Comparable;

这个道理很简单,因为它们传入的就是一个比较器,所以无需实现关于比较的方法。实际上它们两者的比较器中比较的是基本数据类型或者String类型,而基本数据类型和String类型都已经实现了Comparable接口。这就是它们两个无需实现上述接口的表面原因和实际原因。

5.这两种方法都可以对集合中的元素进行排序、降序、自然顺序排序、自然顺序降序、按一个属性排序后继续按另一个属性排序等操作。

// 降序排序
default Comparator<T> reversed() {
    return Collections.reverseOrder(this);
}
// 按照一个属性排序后继续按照另外一个属性排序
default Comparator<T> thenComparing(Comparator<? super T> other) {
    Objects.requireNonNull(other);
    return (Comparator<T> & Serializable) (c1, c2) -> {
        int res = compare(c1, c2);
        return (res != 0) ? res : other.compare(c1, c2);
    };
}
// 自然顺序排序
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
    return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
// 自然顺序降序
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
    return Collections.reverseOrder();
}

使用起来也非常简单:

// 降序
studentList.sort(Comparator.comparing(Student::getAge).reversed());
// 先按照age排序,如果age相同的按照height排序  studentList.sort(Comparator.comparing(Student::getAge)
.thenComparing(Student::getHeight));
// 自然顺序排序
studentList.sort(Comparator.naturalOrder());
// 自然顺序降序
studentList.sort(Comparator.reverseOrder());

切记:自然顺序排序和自然顺序降序这两个方法用来对集合中的元素是基本类型或String类型进行排序,直接将对象进行排序,会没有效果,即使你将对象实现了Comparable接口。上面我的实例中,studentList排序后没有任何效果。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

北冥牧之

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

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

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

打赏作者

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

抵扣说明:

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

余额充值