“顺序“在任何一个领域里都是非常重要的一个概念,程序也不例外。不同的执行顺序,能对你的执行结果产生直接影响。

既然涉及到顺序,那就要求排序。所以本文讨论的就是排序中使用到的比较器Comparable和Comparator。

Comparable和Comparator都是java.包下的两个接口,从字面上看这两个接口都是用来做比较用的,但是jdk里面不可能定义两个功能相同的接口,所以他们肯定有不同的用处。

JDK中的Comparable和 Comparator

Comparable和Comparator接口都是为了对类进行比较,众所周知,诸如Integer,double等基本数据类型,java可以对他们进行比较,而对于类的比较,需要人工定义比较用到的字段比较逻辑。

Comparable

Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些 类是可以和自己比较的。
若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。
此外,**实现此接口的对象可以用作有序映射中的键或有序集合中的集合,无需指定比较器。**该接口定义如下

// @since 1.2 出现得还是稍微偏晚的
public interface Comparable<T> {
// 这里入参也是T 所以是自己和自己比较的
// 规则:this和t比较。 返回0表示两个对象相等
// 返回正数: this > o
// 返回负数: this < o
public int compareTo(T o);
}

比如如下例子:

    public static void main(String[] args) {
Integer[] intArr = {new Integer(2), new Integer(1), new Integer(9), new Integer(5)};
System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(intArr)); //排序前:2,1,9,5
Arrays.sort(intArr);
System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(intArr)); //排序后:1,2,5,9
}

Comparator

Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。
个人认为有两种情况可以使用实现Comparator接口的方式:

对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较(大都是这种情况)
对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式

// @since 1.2  JDK8该接口增加了很多默认方法,后面也会讲解
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}

若一个类要实现Comparator接口:它一定要实现compare(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。
int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。
现在我们自定义一个类Person,然后给Person类自定义一个比较器。

public class Person {
public String name;
public Integer age;
} // person类的Compartor比较器 泛型类型为Person
// 按照
public class PersonCompartor implements Comparator<Person> { @Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
} // 测试
public static void main(String[] args) {
Person[] people = new Person[]{new Person("fsx", 18), new Person("dy", 15)}; System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(people));
Arrays.sort(people, new PersonCompartor()); // 使用自定义的比较器排序
System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(people));
} 结果:
排序前:Person{name='fsx', age=18},Person{name='dy', age=15}
排序后:Person{name='dy', age=15},Person{name='fsx', age=18}

可以看到完全按照我们自己定义的比较器排序了。并且,并且,并且Person是没有实现排序接口的哦,所以**是没有侵入性的**

Comparable和Comparator区别比较

Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。解耦了~~
Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
个性化比较:如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法。
Comparable接口是 java.lang包下的 而 Comparator接口才是java.util包下的。(由此课件后者被归类为一种工具)
两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码。(有侵入性)
用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。

Comparator接口中的默认方法和静态方法

JDK1.8后,在此接口中Comparator定义了好些个静态方法和默认方法,很多时候我们能够当作工具来使用。

default方法属于实例的,static方法属于类的(当然实例也可使用)

    // 逆序排序 用于集合的排序~
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
} // Demo
public static void main(String[] args) {
Person[] people = new Person[]{new Person("fsx", 18), new Person("dy", 15)}; System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(people));
Arrays.sort(people, new PersonCompartor().reversed()); // 使用自定义的比较器排序
System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(people));
} // Demo结果:这样reversed一下 就逆序了~~~
排序前:Person{name='fsx', age=18},Person{name='dy', age=15}
排序后:Person{name='fsx', age=18},Person{name='dy', age=15} // 可以很方便的实现两层排序:比如先按照年龄排序 再按照名字排序等等~~~~
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);
};
}
default <U extends Comparable<? super U>> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor)
{
return thenComparing(comparing(keyExtractor));
}
default <U> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
return thenComparing(comparing(keyExtractor, keyComparator));
}
default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
return thenComparing(comparingInt(keyExtractor));
}
default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
return thenComparing(comparingLong(keyExtractor));
}
default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
return thenComparing(comparingDouble(keyExtractor));
} // Demo:
public static void main(String[] args) {
Person[] people = new Person[]{new Person("fsx", 18), new Person("dy", 15)}; System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(people));
Arrays.sort(people, new PersonCompartor().thenComparing(Person::getName)); // 使用自定义的比较器排序
System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(people));
}

下面看看静态方法:

    public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return Collections.reverseOrder();
}
// 按照自然排序的一个比较器
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
// 它俩是对比较器进行了包装,对null友好了
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(true, comparator);
}
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(false, comparator);
} // Demo
public static void main(String[] args) {
// 放置一个null值
Person[] people = new Person[]{new Person("fsx", 18), null, new Person("dy", 15)}; System.out.println("排序前:" + StringUtils.arrayToCommaDelimitedString(people));
Arrays.sort(people, Comparator.nullsFirst(new PersonCompartor())); // 使用自定义的比较器排序
System.out.println("排序后:" + StringUtils.arrayToCommaDelimitedString(people));
}
// 结果 把null放在了第一位
排序前:Person{name='fsx', age=18},null,Person{name='dy', age=15}
排序后:null,Person{name='dy', age=15},Person{name='fsx', age=18} // 它只需要一个函数,所以要求你取出来的这个字段是实现了Comparable接口的,所以你从泛型约束中也能看出来
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
// 和comparing 方法一不同的是 该方法多了一个参数 keyComparator ,keyComparator 是创建一个自定义的比较器 注意是只比较的是key
// 比如这样子:Arrays.sort(people, Comparator.comparing(Person::getAge, (a1, a2) -> a2 - a1)); // 使用自定义的比较器排序
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
} // Arrays.sort(people, Comparator.comparingInt(Person::getAge)); 他们不能自定义比较器了
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
}
public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
}

Spring中的ComparableComparator和Comparators

备注此处的Comparators指的是Spring包下的:org.springframework.util.comparator.Comparators,因为JDK自带的java.util.Comparators它是不提供给外部访问的。

ComparableComparator

// @since 1.2.2 比较器,用于将可比较器适配比较器接口。
public class ComparableComparator<T extends Comparable<T>> implements Comparator<T> {
// 实例 单例 这样子的话就可以当作Comparator来使用了
@SuppressWarnings("rawtypes")
public static final ComparableComparator INSTANCE = new ComparableComparator();
@Override
public int compare(T o1, T o2) {
return o1.compareTo(o2);
}
}

这个适配类是Spring1.2.2就出来了,但是下面的工具:Comparators可是Spring5.0才提供

Comparators

它是Spring5.0后提供的一个工具类,里面主要是提供了一些静态方法,来提供外部比较器。

// @since 5.0  注意使用它和直接使用Comparator的区别是,它要求你比较的对象都实现了Comparable的   否则都是不适用的
public abstract class Comparators { // 自然排序~~~~
// Arrays.sort(people, Comparators.comparable()); 比如你这么用,是要求peple里面元素实现了Comparable接口的 否则报错
@SuppressWarnings("unchecked")
public static <T> Comparator<T> comparable() {
return ComparableComparator.INSTANCE;
}
// null放在最后
@SuppressWarnings("unchecked")
public static <T> Comparator<T> nullsLow() {
return NullSafeComparator.NULLS_LOW;
}
/// null放最后 并且我们还可以提供一个自定义的比较器
public static <T> Comparator<T> nullsLow(Comparator<T> comparator) {
return new NullSafeComparator<>(comparator, true);
} @SuppressWarnings("unchecked")
public static <T> Comparator<T> nullsHigh() {
return NullSafeComparator.NULLS_HIGH;
}
public static <T> Comparator<T> nullsHigh(Comparator<T> comparator) {
return new NullSafeComparator<>(comparator, false);
}
}

OrderComparator

使用OrderComparator来比较2个对象的排序顺序。注意它用于Spring用来比较实现了Ordered接口的对象。
注意它@since 07.04.2003出现得非常早,所以这个类并不支持@Order注解的排序~~~

但是,PriorityOrdered接口它也是支持的,虽然它Spring2.5才出现。
另外,它是一个Comparator,所以它可以作为自定义比较器放在数组、集合里排序。形如;

public class OrderComparator implements Comparator<Object> {
...
public static void sort(List<?> list) {
if (list.size() > 1) {
list.sort(INSTANCE);
}
}
public static void sort(Object[] array) {
if (array.length > 1) {
Arrays.sort(array, INSTANCE);
}
}
public static void sortIfNecessary(Object value) {
if (value instanceof Object[]) {
sort((Object[]) value);
}
else if (value instanceof List) {
sort((List<?>) value);
}
}
}

AnnotationAwareOrderComparator

AnnotationAwareOrderComparator继承自OrderComparator
其可以同时处理对象实现Ordered接口或@Order注解。

显然它增强了排序能力,不仅支持Ordered接口,还支持到了@Order注解。

@Order注解@since 2.0,AnnotationAwareOrderComparator它@since 2.0.1,几乎同时出现的
它提供了两个静态方法,使用非常广泛,方便我们对数组、即可记性排序:

public class AnnotationAwareOrderComparator extends OrderComparator {

    /**
* 用来检查实现Ordered接口、@Order和@Priority注解
*/
protected Integer findOrder(Object obj) {
// 检查常规的Ordered接口,通过子类重写的getOrder方法返回顺序值
Integer order = super.findOrder(obj);
if (order != null) {
return order;
} // 检查实现@Order和@Priority注解
if (obj instanceof Class) {
// 在类上检查@Order和@Priority注解,并找出顺序值
return OrderUtils.getOrder((Class<?>) obj);
} else if (obj instanceof Method) {
// 在方法上检查@Order注解,并找出顺序值
Order ann = AnnotationUtils.findAnnotation((Method) obj, Order.class);
if (ann != null) {
return ann.value();
}
} else if (obj instanceof AnnotatedElement) {
// 在注解中找@Order注解,并找出顺序值
Order ann = AnnotationUtils.getAnnotation((AnnotatedElement) obj, Order.class);
if (ann != null) {
return ann.value();
}
} else if (obj != null) {
order = OrderUtils.getOrder(obj.getClass());
if (order == null && obj instanceof DecoratingProxy) {
order = OrderUtils.getOrder(((DecoratingProxy) obj).getDecoratedClass());
}
} return order;
} // ================================================================== // 注意此处和上面的区别,它用的是自己的instance
public static void sort(List<?> list) {
if (list.size() > 1) {
list.sort(INSTANCE);
}
}
*/
public static void sort(Object[] array) {
if (array.length > 1) {
Arrays.sort(array, INSTANCE);
}
}
public static void sortIfNecessary(Object value) {
if (value instanceof Object[]) {
sort((Object[]) value);
}
else if (value instanceof List) {
sort((List<?>) value);
}
}
}

OrderUtils

最后介绍这个工具类,它是Spring4.1后提出的。它支持注解@Order和javax.annotation.Priority也是被支持的,因此它的getOrder()方法就是获取order值了。

需要注意的是:先找@Order,若没有再去找@Priority,都没标注就返回默认值

Java中的比较器(排序)的更多相关文章

  1. java中Comparator比较器顺序问题,源码分析

    提示: 分析过程是个人的一些理解,如有不对的地方,还请大家见谅,指出错误,共同学习. 源码分析过程中由于我写的注释比较啰嗦.比较多,导致文中源代码不清晰,还请一遍参照源代码,一遍参照本文进行阅读. 原 ...

  2. Java中常见的排序方法

    本博主要介绍Java中几种常见的排序算法: /* 排序方法的演示1)插入排序(直接插入排序.希尔排序)2)交换排序(冒泡排序.快速排序)3)选择排序(直接选择排序.堆排序)4)归并排序5)分配排序(基 ...

  3. java中字符串的排序(1)

    按照前段时间在快速.冒泡等排序的评论中提到是否可以进行字符串的排序,由于最近有考试,时间比较紧,所以今天才实现此功能.此功能是针对一串字符川进行的实现,运行后的结果如下所示: 具体的程序相对较为简单, ...

  4. Java TreeSet集合 比较器排序Comparator的使用

    比较器排序Comparator的使用 存储学生对象,并遍历,创建TreeSet集合使用带参构造方法 要求,按照学生年龄从小到大排序,如果年龄相同,则按照姓名的字母循序排序 结论 用TreeSet集合存 ...

  5. java中的选择排序之降序排列

    import java.util.Arrays;//必须加载 class Demo{ public static void main(String []args){ int[] arr={3,54,4 ...

  6. Java中List集合排序的方法 比较器的使用 根据学生对象数学 语文 英语成绩总和进行sort排序

    package com.swift; import java.util.ArrayList; import java.util.Collections; import java.util.Compar ...

  7. java中Collections.sort排序详解

    Comparator是个接口,可重写compare()及equals()这两个方法,用于比价功能:如果是null的话,就是使用元素的默认顺序,如a,b,c,d,e,f,g,就是a,b,c,d,e,f, ...

  8. (网页)java中Collections.sort排序详解(转)

    转自CSDN: Comparator是个接口,可重写compare()及equals()这两个方法,用于比价功能:如果是null的话,就是使用元素的默认顺序,如a,b,c,d,e,f,g,就是a,b, ...

  9. [转]java中Collections.sort排序详解

      Comparator是个接口,可重写compare()及equals()这两个方法,用于比价功能:如果是null的话,就是使用元素的默认顺序,如a,b,c,d,e,f,g,就是a,b,c,d,e, ...

随机推荐

  1. 痞子衡嵌入式:了解i.MXRTxxx系列ROM中灵活的串行NOR Flash启动硬复位引脚选择

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRTxxx系列ROM中灵活的串行NOR Flash启动硬复位引脚选择. 关于 i.MXRT 系列 BootROM 中串行 NOR ...

  2. 在deeping上安装mariadb

    1,安装的官网参考:有安装的命令和指导https://downloads.mariadb.org/mariadb/repositories/#distro=Debian&distro_rele ...

  3. JPcap入门

    1,参照入门:安装第一个代码:https://blog.csdn.net/qq_37638061/article/details/80710143 2,数据解析,不可用但有启发意义:https://b ...

  4. 微信小程序云开发-云存储的应用-识别通用印刷体

    一.准备工作 1.创建云函数identify 2.云函数identify中index.js代码 1 // 云函数入口文件 2 const cloud = require('wx-server-sdk' ...

  5. 最小覆盖问题-POJ3041-P1129

    POJ3041 这道题正解对于像我这种蒟蒻来说比较难以想到. 我们发现每次覆盖的只是一条线上的所有点.那么我们可以把它想象成一个二分图,两个集合分别是横轴和纵轴. 想一想,这实际上是不是就是x轴轴和纵 ...

  6. python里面的==,is的区别

    python中对象的三个要素:id(身份标示),type(数据类型).value(值) ==  比较操作符:用来比较两个对象值是否相等. is  同一性运算符:比较两个对象的id值是否相等,即是否是同 ...

  7. 爬取房价信息并制作成柱状图XPath,pyecharts

    以长沙楼盘为例,看一下它的房价情况如何url = https://cs.newhouse.fang.com/house/s/b91/ 一.页面 二.分析页面源代码 我们要获得的数据就是名字和价格,先来 ...

  8. python序列化proto时对repeated修饰数据进行赋值(常用类型和其他类型)

    说一下对proto文件中数据使用时的书写方法,因为笔者也经常弄混淆 一.repeated修饰符,该列表是常用类型,比如int message C2GS_GoodsList { repeated int ...

  9. 构建前端第12篇之---在Vue中对组件,变量,函数的全局引入

    张燕涛写于2020-01-16 星期two 本篇还是源于import和export的使用,昨天看es6入门 和MDN文档,大体上用法了解了,但今天看ElementUI源码的时候,看到 //src/in ...

  10. Spring Cloud分区发布实践(4) FeignClient

    上面看到直接通过网关访问微服务是可以实现按区域调用的, 那么微服务之间调用是否也能按区域划分哪? 下面我们使用FeignClient来调用微服务, 就可以配合LoadBalancer实现按区域调用. ...