Java Comparator使用指南 ---- 看这一篇就够了
目录
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方法:
- 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();(不许套娃!)
- 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使用指南 ---- 看这一篇就够了相关推荐
- 学java日志框架,看这一篇就够了!!!
什么是日志框架 日志框架的选择 Logback的使用与配置 什么是日志框架 是一套能实现日志输出的工具包 能够描述系统运行状态的所有时间都可以算作日志 日志框架的能力 定制输出目标 定制输出格式 携带 ...
- java11模块化开发_【JDK 11】关于 Java 模块系统,看这一篇就够了
继 2014 年 3 月 Java 8 发布之后,时隔 4 年,2018 年 9 月,Java 11 如期发布,其间间隔了 Java 9 和 Java 10 两个非LTS(Long Term Supp ...
- 关于 Java 模块系统,看这一篇就够了
作者 | Emac 杏仁医生架构师兼平台组负责人,关注为服务.DevOps领域. 继 2014 年 3 月 Java 8 发布之后,时隔 4 年,2018 年 9 月,Java 11 如期发布,其间间 ...
- java面试题,看我这篇就够了,前端后台应有尽有,包你通过面试
面试题精华版:https://blog.csdn.net/cencong863251/article/details/88963573 以下为详情版: HTML&CSS部分 1.HTML中定义 ...
- java分布式项目,看这一篇就够了!
三.堆空间 基本描述 JVM启动时创建堆区,是内存管理的核心区,通常情况下也是最大的内存空间,是被所有线程共享的,几乎所有的对象实例都要在堆中分配内存,所以这里也是垃圾回收的重点空间. 堆栈关系 栈是 ...
- Java socket详解,看这一篇就够了
刚给大家讲解Java socket通信后,好多童鞋私信我,有好多地方不理解,看不明白.特抽时间整理一下,详细讲述Java socket通信原理和实现案例.整个过程楼主都是通过先简单明了的示例让大家了解 ...
- Featuretools快速使用指南--看这一篇就够了
Featuretools简单攻略 Featuretools介绍 Featuretools快速开始 Featuretools介绍 人工特性工程是一项冗长乏味的任务,并且受到人类想象力的限制--我们可以思 ...
- Java学习路线图,看这一篇就够了!
主要分为三阶段 | 耗废1024根秀发,Java学习路线图来了,整合了自己所学的所有技术整理出来的2022最新版Java学习路线图,适合于初.中级别的Java程序员.可以按照这个序号来学习的,或者把知 ...
- 收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!
Python Web全栈开发入门实战教程教程 大家好,我叫亓官劼(qí guān jié ),这个<Python Web全栈开发入门实战教程教程>是一个零基础的实战教程,手把手带你开 ...
最新文章
- 用链表生成前序二叉树
- python导入csv文件-Python读写文件(csv、txt、excel)
- 误差模型:过拟合,交叉验证,偏差-方差权衡
- Microsoft重申对F#的支持
- 编辑xml文件时不能自动提示问题的解决
- Java实现string转byte
- Renascence架构原理——最优化算法
- 使用PrimeFaces开发数据导出实用程序
- 使用自定义annotation接口进行aspectj动态缓存
- System.BadImageFormatException: 试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)...
- 企业能源管理系统+用能检测+用能分析+能效诊断+能源调控+能源绩效考核+用能采集+Axure高保真企业web端电网能源管理系统
- mysql查看索引文件_MySql如何查看索引并实现优化
- [ACM] hdu 2177 取(2堆)石子游戏(威佐夫博弈)
- 举例说明Java的反射机制,简单的Java反射机制
- 计算机木马不会主动传播什么疾病,研究如何制作自动运行的木马病毒以及如何传播...
- HeadFirstJava 10数字与静态
- c语言之判断if语句
- Python求解一元二次方程根
- echart x轴自定义间距
- Python的起源与Python之父
热门文章
- idel maven创建springboot
- python 英语分词是什么意思_英语里面现在分词是什么意思
- zh-un电子显示屏优盘发送与设置方法(P10单红)
- --------乱弹琴-------创造价值----侵略--和扩张
- 22-07-14 西安 Git 分布式版本控制系统 、代码管理
- 微信公众平台定制开发
- Logism · 汉字字库存储芯片扩展 实验
- 机械革命 键盘灯 linux,机械革命x6Ti安装ubuntu(100%成功)
- FLASH鼠绘入门教程,主要让大家熟悉一下简单图形的绘制!
- python app mysql_使用App Engin连接到MySQL数据库