目录

Comparator:

Comparator的Default方法:

Comparable接口


在Java学习过程中,Arrays.sort()可以说是我写过最多的一个方法之一。但在很多时候,仅仅是对数组的升序排序并不能满足我们的要求。例如对复杂的对象数组进行排序,或是对大量对象进行分组。为此,Java提供了比较器来解决此类问题.

上来先举个例子:

class  People{String name;int age;int id;public People(String name, int age, int id) {this.name = name;this.age = age;this.id = id;}@Overridepublic String toString() {return "People{" +"name='" + name + '\'' +", age=" + age +", id=" + id +'}';}
}

我们有一个People类,包含Name,age,id三个属性,如果我希望按照每个人的年龄排序的话,单单用sort就无法解决了。此时便要通过实现Comparator类或者Comparable接口来解决此问题。

Comparator:

Comparator接口的定义如下:

public interface Comparator<T> {int compare(T o1, T o2);boolean equals(Object obj);
}

若一个类要实现Comparator接口:它一定要实现compareTo(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。经过网上查阅得知,这是因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。这里可以用反编译去验证,就不在这写了。

int compare(T o1, T o2) 是“比较o1和o2的大小”。当o1<o2时return -1, o1=o2时return 0, o1 > o2时return 1。

那么继续说上面的例子,如果要实现对People进行排序,可以用Comparator实现类:

public static List<People> compareTest(List<People> arr){Collections.sort(arr, new Comparator<People>(){public int compare(People p1,People p2){int a = p1.age;    //比较的是ageint b = p2.age;return a<b ? -1 : a==b ? 0 : 1 ;    //当a<b返回-1,a==b返回0,a>b返回1}});return arr;
}

最后进行测试:

public static void main(String[] args) {List<People> list = new ArrayList<>();list.add(new People("Sayo", 17,2));list.add(new People("Hina", 17,1));list.add(new People("Tsugu", 16,3));list.add(new People("Dio", 100,4));list.add(new People("Naruto", 15,7));list.add(new People("Sasuke", 15,6));compareTest(list);for(People p : list){System.out.println(p.toString());}
}

输出结果,可以看到所有结果是按照年龄由小到大排序:

People{name='Naruto', age=15, id=7}
People{name='Sasuke', age=15, id=6}
People{name='Tsugu', age=16, id=3}
People{name='Sayo', age=17, id=2}
People{name='Hina', age=17, id=1}
People{name='Dio', age=100, id=4}

这时你可能又会问,如果我想有多个比较器呢?你当然可以使用多次Comparator来完成,不过Java8好心的为我们创建了许多default方法,接下来我们来看看都有什么可以使用的方法:

Comparator的Default方法:

  1.  reversed()

    既然有了升序排序,那自然最广泛的实现便是降序排列,Java为我们定义了reversed()方法:

    default Comparator<T> reversed() {return Collections.reverseOrder(this);}

    可以看到这个方法使用的是Collection类的reverseOrder方法,用于返回降序排列。这里闲的蛋疼来撕一撕源码:

    public static <T> Comparator<T> reverseOrder(Comparator<T> cmp) {if (cmp == null) {return (Comparator<T>) ReverseComparator.REVERSE_ORDER;} else if (cmp == ReverseComparator.REVERSE_ORDER) {return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;} else if (cmp == Comparators.NaturalOrderComparator.INSTANCE) {return (Comparator<T>) ReverseComparator.REVERSE_ORDER;} else if (cmp instanceof ReverseComparator2) {return ((ReverseComparator2<T>) cmp).cmp;} else {return new ReverseComparator2<>(cmp);}}

    看到这里返回的都是ReverseComparator,再点进去:

    private static class ReverseComparatorimplements Comparator<Comparable<Object>>, Serializable {@java.io.Serialprivate static final long serialVersionUID = 7207038068494060240L;static final ReverseComparator REVERSE_ORDER= new ReverseComparator();public int compare(Comparable<Object> c1, Comparable<Object> c2) {return c2.compareTo(c1);}@java.io.Serialprivate Object readResolve() { return Collections.reverseOrder(); }@Overridepublic Comparator<Comparable<Object>> reversed() {return Comparator.naturalOrder();}}

    可以看到这里的compare(c1, c2)方法返回的是c2.compareTo(c1), 比较有趣的是,这里如果继续调用reversed(),则会返回naturalOrder();(不许套娃!)

  2. thenComparing(Comparator<? super T> other)
    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);};}

这个方法便是Java8为了简化代码结构整出来的东西。你可以在你的每个Comparator实现类后面接一个.thenComparing。

再拿上面的例子来说,如果我想先对age排序,之后再对id排序,最后再按名字长度我可以这样写:

 public static List<People> compareTest(List<People> arr) {Collections.sort(arr, new Comparator<People>() {public int compare(People p1, People p2) {int a = p1.age;int b = p2.age;return a < b ? -1 : a == b ? 0 : 1;}}.thenComparing(new Comparator<People>() {    //id排序public int compare(People p1, People p2) {return p1.id < p2.id ? -1 : p1.id == p2.id ? 0 : 1;}}.thenComparingInt(x -> x.name.length())    //按名字长度排序));return arr;}

之后进行测试,注意Donald是排在Sasuke前面的,而Sayo也是排在Hina前:

public static void main(String[] args) {List<People> list = new ArrayList<>();list.add(new People("Sayo", 17, 2));list.add(new People("Hina", 17, 1));list.add(new People("Tsugu", 16, 3));list.add(new People("Dio", 100, 4));list.add(new People("Naruto", 15, 7));list.add(new People("Donald.Trashump", 15, 6));list.add(new People("Sasuke", 15, 6));compareTest(list);for (People p : list) {System.out.println(p.toString());}
}

输出结果:

People{name='Sasuke', age=15, id=6}    //可以看到Sasuke因为名字较短而排在Donald之前
People{name='Donald.Trashump', age=15, id=6}
People{name='Naruto', age=15, id=7}
People{name='Tsugu', age=16, id=3}
People{name='Hina', age=17, id=1}
People{name='Sayo', age=17, id=2}    //可以看到Sayo因为id比较大,排在Hina之后
People{name='Dio', age=100, id=4}

可以看到先按照age排序,在其基础上对age相同的对象id进行比较,最后再对两者均相同的进行姓名长度的比较。

同时他也支持函数式接口:

default <U> Comparator<T> thenComparing(Function<? super T, ? extends U> keyExtractor,Comparator<? super U> keyComparator){return thenComparing(comparing(keyExtractor, keyComparator));}

这个方法还有很多变种,thenComparingInt、thenComparingLong、thenComparingDouble,顾名思义,就不做解释了

Comparable接口

这个比较简单,点进Comparable接口中发现只有一个方法compareTo,只要在需要排序的类上实现comparable接口,并重写compareTo方法即可:

依旧是People:

public class ComparableTest {static class  People implements Comparable{String name;int age;int id;public People(String name, int age, int id) {this.name = name;this.age = age;this.id = id;}@Overridepublic String toString() {return "People{" +"name='" + name + '\'' +", age=" + age +", id=" + id +'}';}@Overridepublic int compareTo(Object o1) {    //重写compareTo接口People o = (People)o1;return this.age < o.age ? -1 : this.age == o.age ? 0 : 1;    //对年龄进行比较}}public static void main(String[] args) {List<People> list = new ArrayList<>();list.add(new People("Sayo", 17, 2));list.add(new People("Hina", 17, 1));list.add(new People("Tsugu", 16, 3));list.add(new People("Dio", 100, 4));list.add(new People("Naruto", 15, 7));list.add(new People("Donald.Trashump", 15, 6));list.add(new People("Sasuke", 15, 6));Collections.sort(list);for (People p : list) {System.out.println(p.toString());}}
}

输出结果按年龄排序:

People{name='Naruto', age=15, id=7}
People{name='Donald.Trashump', age=15, id=6}
People{name='Sasuke', age=15, id=6}
People{name='Tsugu', age=16, id=3}
People{name='Sayo', age=17, id=2}
People{name='Hina', age=17, id=1}
People{name='Dio', age=100, id=4}

Java Comparator使用指南 ---- 看这一篇就够了相关推荐

  1. 学java日志框架,看这一篇就够了!!!

    什么是日志框架 日志框架的选择 Logback的使用与配置 什么是日志框架 是一套能实现日志输出的工具包 能够描述系统运行状态的所有时间都可以算作日志 日志框架的能力 定制输出目标 定制输出格式 携带 ...

  2. java11模块化开发_【JDK 11】关于 Java 模块系统,看这一篇就够了

    继 2014 年 3 月 Java 8 发布之后,时隔 4 年,2018 年 9 月,Java 11 如期发布,其间间隔了 Java 9 和 Java 10 两个非LTS(Long Term Supp ...

  3. 关于 Java 模块系统,看这一篇就够了

    作者 | Emac 杏仁医生架构师兼平台组负责人,关注为服务.DevOps领域. 继 2014 年 3 月 Java 8 发布之后,时隔 4 年,2018 年 9 月,Java 11 如期发布,其间间 ...

  4. java面试题,看我这篇就够了,前端后台应有尽有,包你通过面试

    面试题精华版:https://blog.csdn.net/cencong863251/article/details/88963573 以下为详情版: HTML&CSS部分 1.HTML中定义 ...

  5. java分布式项目,看这一篇就够了!

    三.堆空间 基本描述 JVM启动时创建堆区,是内存管理的核心区,通常情况下也是最大的内存空间,是被所有线程共享的,几乎所有的对象实例都要在堆中分配内存,所以这里也是垃圾回收的重点空间. 堆栈关系 栈是 ...

  6. Java socket详解,看这一篇就够了

    刚给大家讲解Java socket通信后,好多童鞋私信我,有好多地方不理解,看不明白.特抽时间整理一下,详细讲述Java socket通信原理和实现案例.整个过程楼主都是通过先简单明了的示例让大家了解 ...

  7. Featuretools快速使用指南--看这一篇就够了

    Featuretools简单攻略 Featuretools介绍 Featuretools快速开始 Featuretools介绍 人工特性工程是一项冗长乏味的任务,并且受到人类想象力的限制--我们可以思 ...

  8. Java学习路线图,看这一篇就够了!

    主要分为三阶段 | 耗废1024根秀发,Java学习路线图来了,整合了自己所学的所有技术整理出来的2022最新版Java学习路线图,适合于初.中级别的Java程序员.可以按照这个序号来学习的,或者把知 ...

  9. 收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!

    Python Web全栈开发入门实战教程教程    大家好,我叫亓官劼(qí guān jié ),这个<Python Web全栈开发入门实战教程教程>是一个零基础的实战教程,手把手带你开 ...

最新文章

  1. 用链表生成前序二叉树
  2. python导入csv文件-Python读写文件(csv、txt、excel)
  3. 误差模型:过拟合,交叉验证,偏差-方差权衡
  4. Microsoft重申对F#的支持
  5. 编辑xml文件时不能自动提示问题的解决
  6. Java实现string转byte
  7. Renascence架构原理——最优化算法
  8. 使用PrimeFaces开发数据导出实用程序
  9. 使用自定义annotation接口进行aspectj动态缓存
  10. System.BadImageFormatException: 试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)...
  11. 企业能源管理系统+用能检测+用能分析+能效诊断+能源调控+能源绩效考核+用能采集+Axure高保真企业web端电网能源管理系统
  12. mysql查看索引文件_MySql如何查看索引并实现优化
  13. [ACM] hdu 2177 取(2堆)石子游戏(威佐夫博弈)
  14. 举例说明Java的反射机制,简单的Java反射机制
  15. 计算机木马不会主动传播什么疾病,研究如何制作自动运行的木马病毒以及如何传播...
  16. HeadFirstJava 10数字与静态
  17. c语言之判断if语句
  18. Python求解一元二次方程根
  19. echart x轴自定义间距
  20. Python的起源与Python之父

热门文章

  1. idel maven创建springboot
  2. python 英语分词是什么意思_英语里面现在分词是什么意思
  3. zh-un电子显示屏优盘发送与设置方法(P10单红)
  4. --------乱弹琴-------创造价值----侵略--和扩张
  5. 22-07-14 西安 Git 分布式版本控制系统 、代码管理
  6. 微信公众平台定制开发
  7. Logism · 汉字字库存储芯片扩展 实验
  8. 机械革命 键盘灯 linux,机械革命x6Ti安装ubuntu(100%成功)
  9. FLASH鼠绘入门教程,主要让大家熟悉一下简单图形的绘制!
  10. python app mysql_使用App Engin连接到MySQL数据库