java compareto方法怎么排序的_深入理解Java中Comparable和Comparator排序
本文有牛旦教育原创,头条首发,转载注明来源。
如何为需要的排序算法选择正确的接口?通过本文的分析讲解,我们会找到答案参考答案。
程序员经常需要将数据库中的元素排序为集合、数组或映射。在Java中,我们可以实现任何类型的排序算法。使用Comparable接口和compareTo()方法,我们可以使用字母顺序、字符串长度、反字母顺序或数字进行排序。Comparator接口允许我们以更灵活的方式做同样的事情。
概括而言,无论我们想做什么,只需要知道如何为给定的接口和类型实现正确的排序逻辑就可以了
1.Comparable
1.1自定义对象列表排序
在这个示例中,我们使用实现了Comparable接口的POJO类,名为Figure,并在泛型类型中使用Figure:
public class Figure implements Comparable
注意,我们已经覆盖了compareTo()方法,并传入了另一个Figure对象。我们还覆盖了toString()方法,只是为了使示例更容易阅读。
toString方法显示了来自对象的信息。当我们打印对象时,输出为toString()中实现的任何内容。
1.2compareTo()方法
compareTo()方法将给定的对象或当前实例与指定的对象进行比较,以确定对象的顺序。下面快速看一下compareTo()是如何工作的:
我们对实现Comparable的类使用sort()方法排序。如果我们试图传递一个没有实现Comparable的Figure,我们将收到一个编译错误。
sort()方法通过传递任何可比较的对象来使用多态性。然后对象将按预期排序。
前面代码的输出为:
Guanyin Nazha Rulai Wukong
如果我们想反序显示,只要把Collections.sort(figures)换成Collections. reverse(figures)即可,显示结果如下:
Wukong Rulai Nazha Guanyin
1.3排序Java数组
在Java中,只要数组类型实现了Comparable接口,我们就可以用任何类型对数组进行排序。例如:
package com.nd.tutorial.lesson001;import java.util.Arrays;public class ArraySorting { public static void main(String[] args) { int[] mps = new int[] { 9, 8, 7, 6, 1 }; Arrays.sort(mps); Arrays.stream(mps).forEach(System.out::print); Figure[] figures = new Figure[] { new Figure("Nezha"), new Figure("Rulai") }; Arrays.sort(figures); System.out.println(); Arrays.stream(figures).forEach(System.out::println); }}
第一个sort()调用,数组排序成如下形式:
16789
第二个sort()调用,被排序成如下形式:
Nezha
Rulai
请记住,定制对象必须实现Comparable才能进行排序,即使作为数组也是如此。
1.4没有实现Comparable能排序吗?
如果Figure对象没有实现Comparable,IDE将提示语法错误,可以自己试试,类是如下::
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method sort(List) in the type Collections is not applicable for the arguments (List)
at nd.tutorial/com.nd.tutorial.lesson001.FigureSorting.main(FigureSorting.java:15)
这个日志有点混乱,但是不用担心。只要记住,对于没有实现Comparable接口的任何排序对象,那样去排序都会出错。
1.5用TreeMap排序Map
Java API包含了许多用来辅助排序的类,包括TreeMap。在下面的示例中,我们使用TreeMap将键排序到Map中。
package com.nd.tutorial.lesson001;import java.util.Map;import java.util.TreeMap;public class TreeMapExample { public static void main(String[] args) { Map figureCharacter = new TreeMap<>(); figureCharacter.put(new FigureCharacter ("Nezha"), "Circle"); figureCharacter.put(new FigureCharacter ("Rulai"), "FiveMountain"); figureCharacter.put(new FigureCharacter ("Wukong"), "goldencudgel"); figureCharacter.put(new FigureCharacter ("Guanyin"), "Bottle"); System.out.println(figureCharacter); }}
TreeMap使用了实现Comparable接口的compareTo()方法。结果Map中的每个元素都按其键排序。在这种情况下,输出为:
{Guanyin=Bottle, Nezha=Circle, Rulai=FiveMountain, Wukong=goldencudgel}
但是请记住:如果对象没有实现Comparable,将错误。
1.6用TreeSet排序Set
Set接口负责存储唯一的值(不重复的元素值),但当我们使用TreeSet实现时,插入的元素将在我们添加它们时自动排序:
package com.nd.tutorial.lesson001;import java.util.Set;import java.util.TreeSet;public class TreeSetExample { public static void main(String[] args) { Set figureCharacters = new TreeSet<>(); figureCharacters.add(new FigureCharacter ("Wukong")); figureCharacters.add(new FigureCharacter ("Guanyin")); figureCharacters.add(new FigureCharacter ("Nazha")); figureCharacters.add(new FigureCharacter ("Rulai")); System.out.println(figureCharacters); }}
代码输出结果为:
[Guanyin, Nazha, Rulai, Wukong]
同样,如果我们使用一个非Comparable的对象(没实现Comparable接口),将抛出一个错误。
2.用Comparator排序
如果我们不想使用POJO类中的相同compareTo()方法怎么办?我们是否可以覆盖Comparable方法来使用不同的逻辑呢?看下面例子:
public class BadExampleOfComparable { public static void main(String[] args) { List characters = new ArrayList<>(); FigureCharacter guanyin = new FigureCharacter ("Guanyin") { @Override public int compareTo(FigureCharacter figure) { return this.name.length() - (figure.name.length()); } }; FigureCharacter nezha = new FigureCharacter ("Nazha") { @Override public int compareTo(FigureCharacter figure) { return this.name.length() - (figure.name.length()); } }; characters.add(guanyin); characters.add(nezha); Collections.sort(characters); System.out.println(characters); }}
正如您所见,这段代码很复杂,包含很多重复。对于相同的逻辑,我们必须两次重写compareTo()方法。如果有更多的元素,我们将不得不为每个对象复制逻辑。
幸运的是,我们有Comparator接口,它允许我们从Java类中分离compareTo()逻辑。使用Comparator重写上面的例子:
public class GoodExampleOfComparator { public static void main(String[] args) { List characters = new ArrayList<>(); FigureCharacter guanyin = new FigureCharacter ("Guanyin"); FigureCharacter nezha = new FigureCharacter ("Nezha"); characters.add(guanyin); characters.add(nezha); Collections.sort(characters, (Comparator.< FigureCharacter > comparingInt(character1 -> character1.name.length()) .thenComparingInt(character2 -> character2.name.length()))); System.out.println(characters); }}
这些例子演示了Comparable和Comparator之间的主要区别。
当使用Comparable时,对象只有一个默认比较。当您需要处理现有的compareTo()时,或者当您需要以更灵活的方式使用特定的逻辑时,请使用Comparator。Comparator从对象中分离排序逻辑,并在sort()方法中包含compareTo()逻辑。
2.1匿名内部类方式使用Comparator
在下面示例中,我们使用一个匿名内部类来比较对象的值。在本例中,匿名内部类是实现Comparator的任何类。使用它意味着我们不必实例化实现接口的已命名类;相反,我们在匿名内部类中实现compareTo()方法。
public class MarvelComparator { public static void main(String[] args) { List marvelHeroes = new ArrayList<>(); marvelHeroes.add("SpiderMan "); marvelHeroes.add("Wolverine "); marvelHeroes.add("Xavier "); marvelHeroes.add("Cyclops "); Collections.sort(marvelHeroes, new Comparator() { @Override public int compare(String hero1, String hero2) { return hero1.compareTo(hero2); } }); Collections.sort(marvelHeroes, (m1, m2) -> m1.compareTo(m2)); Collections.sort(marvelHeroes, Comparator.naturalOrder()); marvelHeroes.forEach(System.out::print); }}
关于匿名内部类:
匿名内部类就是任何名称无关紧要的类,它实现了我们声明的接口。在这个例子中,new Comparator实际上是一个没有名称的类的实例化,它用我们想要的逻辑实现了这个方法。
2.2lambda表达式方式用Comparator
匿名内部类比较冗长,这可能会导致代码中出现问题。在Comparator接口中,我们可以使用lambda表达式来简化代码,使代码更容易阅读。如下改变,即把:
Collections.sort(marvel, new Comparator() { @Override public int compare(String hero1, String hero2) { return hero1.compareTo(hero2); } });
改成这样:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
代码少了很多,但结果一样。
输出结果如下:
Cyclops SpiderMan Wolverine Xavier
我们还可把代码改的更简单,把:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
改成:
Collections.sort(marvel, Comparator.naturalOrder());
3.核心Java类是Comparable的吗?
许多核心Java类和对象实现了Comparable接口,这意味着我们不必为这些类实现compareTo()逻辑。下面是一些常见的例子:
String
public final class String implements java.io.Serializable, Comparable, CharSequence { ...
Integer
public final class Integer extends Number implements Comparable { …
Double
public final class Double extends Number implements Comparable {...
还有很多其他的。推荐你去探索Java核心类,以了解它们的重要模式和概念。
接收Comparable的挑战
通过理解以下代码的输出来检验所学内容掌握如何。记住,如果你仅仅通过学习就能自己解决这个挑战,你会学得很好。也可运行下面程序进一步理解吸收这些概念。
public class SortComparableChallenge { public static void main(String[] args) { Set
上面程序代码的输出是哪一个:
A.
Baibianjun
Honghaier
Laoshujing
Meixian
Mowangniu
B.
Meixian
Baibianjun
Honghaier
Laoshujing
Mowangniu
C.
Mowangniu
Meixian
Laoshujing
Honghaier
Baibianjun
D.
不知道
TreeSet and reverse()
如代码所示,注意到的第一件事是我们使用了一个TreeSet,因此元素将自动排序。第二件事是比较的顺序是颠倒的,所以排序的顺序是颠倒的。
当我们第一次向列表中添加元素时,TreeSet会自动将它们排序为:
Mowangniu, Meixian, Laoshujing, Honghaier, Baibianjun
然后我们使用reverse()方法,它颠倒元素的顺序。所以最终的输出是:
Baibianjun
Honghaier
Laoshujing
Meixian
Mowangniu
使用Comparable常见错误
ü 试图在sort()方法中对不可比较的对象排序。
ü 在同一对象中对不同的排序策略使用Comparable。
ü 在compareTo()方法中反转比较,以便排序将按相反的顺序进行,如下所示:
正常排序
public int compareTo(Figure figure) {this.name.compareTo(figure.name);}
反转排序
public int compareTo(Simpson simpson) {simpson.name.compareTo(this.name);}
ü 在TreeSet或TreeMap对象中添加非可比对象(没实现Comparable的任何对象)。
小结
关于使用Java排序,需要记住的:
Ø 当比较是给定类的标准比较时,使用Comparable。
Ø 当您需要更多的灵活性时,使用Comparator。
Ø 可以将lambdas与Comparator一起使用。
Ø 许多Java核心类实现了Comparable。
Ø 对Map或Set排序时,使用TreeMap或TreeSet。
Ø compareTo()方法同时适用于Comparable和Comparator。
Ø 如果一个对象大于另一个对象,compareTo()方法将返回一个正数,如果小则返回一个负数,如果两个对象相同,则返回零。
java compareto方法怎么排序的_深入理解Java中Comparable和Comparator排序相关推荐
- java实例化类之后如何赋值_深入理解Java对象的创建过程:类的初始化与实例化...
摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类 ...
- java的跨平台特性是指_如何理解JAVA的跨平台特性
1.java的跨平台,是指java在运行时是凌驾于os之上,是在jvm中运行的,跟os没有直接联系. 2.java跨平台主要是由java的编译方式决定的,因为java是通过jvm先编译再执行,它编译的 ...
- java方法区内存泄露_深入理解java虚拟机-第二章:java内存区域与内存泄露异常...
2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...
- java类二次加载_深入理解java之类加载器
一.类与类加载器 类加载器:实现加载阶段的第一步,通过一个类的全限定名来将这个类的二进制字节流加载进jvm. 类与类加载器:任意一个类唯一性都是由它本身和加载它的类加载器确定,两个类是否相等在它们是由 ...
- java虚拟机和内存的关系_深入理解java虚拟机(linux与jvm内存关系)
本文转载自美团技术团队发表的同名文章 https://tech.meituan.com/linux-jvm-memory.html 一, linux与进程内存模型 要理解jvm最重要的一点是要知道jv ...
- java内存分配和垃圾回收_深入理解java虚拟机(二)垃圾收集器与内存分配策略...
垃圾收集器与内存分配策略 垃圾收集,三个步骤 什么时候收集,收集那些,怎么收集 1.收集那些 我们会将一些不使用的对象进行收集,进行回收内存空间,我们怎么知道呢 1.引用计数法 如果这个实例被其他地方 ...
- java装箱与拆箱原理_深入理解Java中的装箱和拆箱
前言 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱.拆箱相关的问题. 若有不 ...
- Java基本数据类型的自动转换_彻底理解Java中的基本数据类型转换(自动、强制、提升)...
说基本数据类型转换之前,先了解下 Java 中的 8 种基本数据类型,以及它们的占内存的容量大小和表示的范围,如下图所示. 重新温故了下原始数据类型,现在来解释下它们之间的转换关系. 自动类型转换 自 ...
- java comparator相等_详解Java中Comparable和Comparator接口的区别
详解Java中Comparable和Comparator接口的区别 发布于 2020-7-20| 复制链接 摘记: 详解Java中Comparable和Comparator接口的区别本文要来详细分析一 ...
最新文章
- 深度学习多框架多平台推理引擎工具
- str、tuple、dict之间的相互转换
- hashmap::begin() 坑
- 安卓APP_ 控件(9)—— PopupWindow弹窗
- 全球100款大数据工具汇总(前50款)
- 休眠锁定模式– PESSIMISTIC_FORCE_INCREMENT锁定模式如何工作
- a类网络被谁用了_谁说学考没有用?浙江三位一体报名要求,需要几A几B都在这里...
- 基于beego一键创建RESTFul应用
- (88)FPGA写文件激励(fwrite)
- 微软在 ARM 上成功移植 OpenJDK for Windows 10
- 学会使用JDK API
- Android中startActivity中的permission检测与UID机制
- ComicEnhancerPro 系列教程
- python中间件有哪些_python_21(Django中间件)
- springboot整合shiro之thymeleaf使用shiro标签
- CE修改器教程总结2
- 矩阵求导术(二)——矩阵对矩阵的求导
- fans域名启动全球调价
- c语言生成exe文件,打开exe文件闪退怎么办
- 小饶学编程之JAVA SE第二部分——Web 前端基础:09CSS3
热门文章
- 和与余数的和同余理解_每日一题 | 第38期:数量关系之余数特性
- 还剩10天,赶紧登下百度网盘,拯救你的2T存储空间吧!
- Nginx配置中一个不起眼字符/的巨大作用,失之毫厘谬以千里
- 扫码登录是如何实现的?
- Nginx 一个牛 X 的功能,流量拷贝!
- java流处理为什么快_“任何情况下,都不可以堕胎”是道德普遍主义的观点。
- mysql菜鸟教程update_PHP MySQL Update
- flink 三种时间机制_360深度实践:Flink 与 Storm 协议级对比
- move函数c语言,C++11 move()函数:将左值强制转换为右值
- 百度小程序opencard书法字典名家书法等测试日志记录