记录一下项目上面遇到的问题
一、 项目在进行升序排序的时候,一定要注意看升序排列的值是否是String类型的,如果String类型的要强转为Integer类型
在这里插入图片描述因为在写期次比较的时候没有注意StageNo期次的类型导致排序时没有按照数字大小进行排序
如果你要排列的数字被定义为String类型的,那就要注意了String字符串是按照ASCII编码进行排序的,在转换时要进行强转
1.1 插个题外话:这里记录下Comparator.comparing()的使用方法:经常用到
list.sort(Comparator.comparing(NlOdIntDetail::getStageNo));
List<NlOdIntDetail> details = list.stream().sorted(Comparator.comparing(NlOdIntDetail::getStageNo)).collect(Collectors.toList());
1.2 Comparator内部比较器和Comparable外部比较器
两者排序比较的方式一样,集合是对元素本身进行排序,通过stream流对元素进行排序,集合本身不会改变,而是将元素的位置赋值给另一个
在这种情况下,我们通过比较器来使用元素的自然顺序。对于自然顺序,元素类需要实现Comparable和覆盖compareTo方法。所有的空元素将被排在最后,非空元素将被排在其自然顺序上。
在这种情况下,我们将null传递给nullsLast方法。我们知道,如果指定给nullsLast的比较器是null,那么返回的比较器认为所有非null元素都是相等的。所有的空元素将被排在最后,对非空元素的顺序没有影响。
比较时我们经常使用Comparable接口中的compareTo方法来设置我们默认的比较方法,而可以通过comparator接口的引用实现额外的比较方法
comparable比较的方法比较简单,但是对代码有较强的侵略性;
comparator可以额外通过实现外部类去引用comparator接口,通过该接口的一个匿名类对象当作参数传递给Collections.sort()或者Arrays.sort()方法
comparator的包用的是java.util包,comparable用的是java.lang包
nullsFirst、nullsLast - 将null值放在第一个或者最后一个
我们的集合中有一个空元素。在排序中,由于nullsLast方法的存在,空元素将排在最后。非空元素的顺序将由传递给nullsLast方法的比较器决定。
我们有一个以上的空元素。我们知道,当两个元素都是空的时候,那么它们被认为是相等的。所以,所有的空元素将被排在最后。非空元素的顺序将由传递给nullsLast方法的比较器决定。
在这种情况下,我们将指定的比较器反转到nullsLast方法。这将只影响非空元素的顺序。所有的空元素将被排在最后。
在这种情况下,我们将nullsLast方法返回的比较器反转。现在,所有的空元素将被排在第一位。
- comparable接口必须重写comparaTo(T a,T b)方法
- comparator接口需要重写compara(T o)方法
- comparable是内在比较器,实现这个接口的类是可以直接比较:this.comparaTo(this),这个类也支持排序,由该类对象做成的集合可以直接使用Collections.sort()方法排序,此外“实现Comparable接口的类的对象”可以用作“有序映射(如Treemap)”中的键或有序集合(TreeSet)中的元素,而不需要指定比较器;comparator是外在比较器,如果想比较两个类又没有实现Comparable或者想实现自定义排序的用comparator
- comparator和comparable同时存在的话,comparator的优先级更高
- comparable需要实现对定义的类进行修改,属于自然排序,而comparator是不用修改原先的类的实现一个新的比较器,comparator用的范围更广
comparator比较器写在类的外部,新定义类比较器,类名随意,是定义在外部,重写compare方法,我们把这个称作外部比较器
package java.util
public interface comparator<T>{
int compare(T a,T b){
//a>b 返回正整数
//a<b 返回负整数
//a=b 返回0
}
}
comparable内部比较器
comparable对代码的侵略性较强,需要实体类实现comparable接口并实现comparaTo方法,单例模式下不可用
package java.lang
public interface comparable<T>{
int comparaTo(T o){
//如果此对象(调用比较器方法的对象)大于指定对象,返回正整数
//如果此对象(调用比较器方法的对象)小于指定对象,返回负整数
//如果此对象等于指定对象,返回0
}
}
内部比较器Comparable实现comparaTo
private int id;// 员工编号
private double salary;// 员工薪资
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Employee(int id, double salary) {
super();
this.id = id;
this.salary = salary;
}
// 为了输出方便,重写toString方法
@Override
public String toString() {
// 简单输出信息
return "id:" + id + ",salary=" + salary;
}
@Override
public int compareTo(Employee o) {
//比较员工编号
int result = this.id > o.id ? 1 :(this.id == o.id ? 0 : -1) ;
//如果编号相等,则比较薪资
if(result == 0){
//比较员工薪资,如果此对象的薪资大于、等于、小于 指定对象,则返回1、0、-1
result = this.salary > o.salary ? 1:(this.salary == o.salary ? 0 :-1);
}
return result;
}
class EmployeeComparable implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
// 比较员工编号,如果此对象的编号大于、等于、小于指定对象,则返回1、0、-1
int result = o1.getId() > o2.getId() ? 1 : (o1.getId() == o2.getId() ? 0 : -1);
// 如果编号相等,则比较薪资
if (result == 0) {
// 比较员工薪资,如果此对象的薪资大于、等于、小于指定对象,则返回1、0、-1
result = o1.getSalary() > o2.getSalary() ? 1 : (o1.getSalary() == o2.getSalary() ? 0 : -1);
}
return result;
}
}
List<Integer> list = asList(1, 2, 3, 4, 5, 6, 67, 78, 98, 100, 1, 3, 2, 6);
//对于元素本身进行升序排序
Collections.sort(list);
System.out.println(list);
这里对于排序的数据类型一定要是整形,如果是String类型是按照ASCII编码进行排序
1.2 接下来我们来说一下Collections.sort 和 list.sort 和 list.stream.sort的区别
1.2.1 第一种就是上面我们介绍的那种Comparable自然排序
Collections.sort(list)
使用Comparator接口时
Collections.sort(list,(o1,o2) -> {return o1.getAge() - o2.getAge();});
或者更明确的写法
Collections.sort(list,new Comparator<Student>(){
@Override
public int compare(Student o1,Student o2){
return o1.getAge - o2.getAge;
}
})
1.2.2 第二种
list本身集合的排序
list.sort(Comparator.comparing(Student::getAge).reverse());
或者使用jdk8的新特性
list.stream().sorted(Comparator.comparating(Student::getAge).reversed()).collect(Collections.toList());
list.stream().sorted(Comparator.comparing(NlOdIntDetail::getStageNo,Comparator.nullsLast(String::compareTo))).collect(Collectors.toList());
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,即无返回值。
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();
}
1、compare - 正常比较
2、naturalOrder - 自然比较,根据实体类定义的Comparable
3、nullsFirst、nullsLast - 将null值放在第一个或者最后一个
4、comparing、comparingLong、comparingInt、comparingDouble - 常用比较方法,可以指定参数类型
5、reversed、reverseOrder - 反转
6、thenComparing、thenComparingDouble、thenComparingInt、thenComparingLong - 多次比较