在工作中我们经常需要对集合中的元素进行比较排序,下面就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接口不同的是:
- Comparator位于包java.util下,而Comparable位于包java.lang下。
- Comparable接口将比较代码嵌入需要进行比较的类的自身代码中,而Comparator接口在一个独立的类中实现比较。
- 如果前期类的设计没有考虑到类的Compare问题而没有实现Comparable接口,后期可以通过Comparator接口来实现比较算法进行排序,并且为了使用不同的排序标准做准备,比如:升序、降序。
- Comparable接口强制进行自然排序,而Comparator接口不强制进行自然排序,可以指定排序顺序。
上面的两种写法最后的方法reversed就是对获取到的属性(age)进行降序排序。
上面的①和②有一定的区别:
- ①是List接口再在dk8中添加的默认方法,位于java.util包下;而sorted方法是jdk8新添加的stream接口,位于java.util.stream包下面;
- 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排序后没有任何效果。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/2742.html