本文有牛旦教育原创,头条首发,转载注明来源。

如何为需要的排序算法选择正确的接口?通过本文的分析讲解,我们会找到答案参考答案。

程序员经常需要将数据库中的元素排序为集合、数组或映射。在Java中,我们可以实现任何类型的排序算法。使用Comparable接口和compareTo()方法,我们可以使用字母顺序、字符串长度、反字母顺序或数字进行排序。Comparator接口允许我们以更灵活的方式做同样的事情。

概括而言,无论我们想做什么,只需要知道如何为给定的接口和类型实现正确的排序逻辑就可以了

1.Comparable

1.1自定义对象列表排序

在这个示例中,我们使用实现了Comparable接口的POJO类,名为Figure,并在泛型类型中使用Figure:

public class Figure implements Comparable
{ String name ; Figure(String name) { this.name = name; } @Override public int compareTo(Figure figure) { return this.name.compareTo(figure.name); }@Override public String toString() { return name ; }}class FigureCharacter implements Comparable{ String name; FigureCharacter(String name) { this.name = name; } @Override public int compareTo(FigureCharacter fc) { return this.name.compareTo(fc.name); } @Override public String toString() { return name ; } }package com.nd.tutorial.lesson001;import java.util.ArrayList;import java.util.Collections;import java.util.List;public class FigureSorting { public static void main(String[] args) { List figures = new ArrayList<>(); figures.add(new FigureCharacter ("Nazha ")); figures.add(new FigureCharacter ("Wukong ")); figures.add(new FigureCharacter ("Guanyin ")); figures.add(new FigureCharacter ("Rulai ")); Collections.sort(figures); figures.stream().map(s -> s.name).forEach(System.out::print); Collections.reverse(figures); figures.stream().forEach(System.out::print); } }

注意,我们已经覆盖了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
set = new TreeSet<>(); set.add(new Figure ("Honghaier")); set.add(new Figure ("Mowangniu")); set.add(new Figure ("Laoshujing")); set.add(new Figure ("Baibianjun")); set.add(new Figure ("Meixian")); List< Figure > list = new ArrayList<>(); list.addAll(set); Collections.reverse(list); list.forEach(System.out::println); } static class Figure implements Comparable< Figure > { String name; public Figure (String name) { this.name = name; } public int compareTo(Figure newday) { return newday.name.compareTo(this.name); } public String toString() { return this.name; } }}

上面程序代码的输出是哪一个:

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排序相关推荐

  1. java实例化类之后如何赋值_深入理解Java对象的创建过程:类的初始化与实例化...

    摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类 ...

  2. java的跨平台特性是指_如何理解JAVA的跨平台特性

    1.java的跨平台,是指java在运行时是凌驾于os之上,是在jvm中运行的,跟os没有直接联系. 2.java跨平台主要是由java的编译方式决定的,因为java是通过jvm先编译再执行,它编译的 ...

  3. java方法区内存泄露_深入理解java虚拟机-第二章:java内存区域与内存泄露异常...

    2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...

  4. java类二次加载_深入理解java之类加载器

    一.类与类加载器 类加载器:实现加载阶段的第一步,通过一个类的全限定名来将这个类的二进制字节流加载进jvm. 类与类加载器:任意一个类唯一性都是由它本身和加载它的类加载器确定,两个类是否相等在它们是由 ...

  5. java虚拟机和内存的关系_深入理解java虚拟机(linux与jvm内存关系)

    本文转载自美团技术团队发表的同名文章 https://tech.meituan.com/linux-jvm-memory.html 一, linux与进程内存模型 要理解jvm最重要的一点是要知道jv ...

  6. java内存分配和垃圾回收_深入理解java虚拟机(二)垃圾收集器与内存分配策略...

    垃圾收集器与内存分配策略 垃圾收集,三个步骤 什么时候收集,收集那些,怎么收集 1.收集那些 我们会将一些不使用的对象进行收集,进行回收内存空间,我们怎么知道呢 1.引用计数法 如果这个实例被其他地方 ...

  7. java装箱与拆箱原理_深入理解Java中的装箱和拆箱

    前言 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱.拆箱相关的问题. 若有不 ...

  8. Java基本数据类型的自动转换_彻底理解Java中的基本数据类型转换(自动、强制、提升)...

    说基本数据类型转换之前,先了解下 Java 中的 8 种基本数据类型,以及它们的占内存的容量大小和表示的范围,如下图所示. 重新温故了下原始数据类型,现在来解释下它们之间的转换关系. 自动类型转换 自 ...

  9. java comparator相等_详解Java中Comparable和Comparator接口的区别

    详解Java中Comparable和Comparator接口的区别 发布于 2020-7-20| 复制链接 摘记: 详解Java中Comparable和Comparator接口的区别本文要来详细分析一 ...

最新文章

  1. 深度学习多框架多平台推理引擎工具
  2. str、tuple、dict之间的相互转换
  3. hashmap::begin() 坑
  4. 安卓APP_ 控件(9)—— PopupWindow弹窗
  5. 全球100款大数据工具汇总(前50款)
  6. 休眠锁定模式– PESSIMISTIC_FORCE_INCREMENT锁定模式如何工作
  7. a类网络被谁用了_谁说学考没有用?浙江三位一体报名要求,需要几A几B都在这里...
  8. 基于beego一键创建RESTFul应用
  9. (88)FPGA写文件激励(fwrite)
  10. 微软在 ARM 上成功移植 OpenJDK for Windows 10
  11. 学会使用JDK API
  12. Android中startActivity中的permission检测与UID机制
  13. ComicEnhancerPro 系列教程
  14. python中间件有哪些_python_21(Django中间件)
  15. springboot整合shiro之thymeleaf使用shiro标签
  16. CE修改器教程总结2
  17. 矩阵求导术(二)——矩阵对矩阵的求导
  18. fans域名启动全球调价
  19. c语言生成exe文件,打开exe文件闪退怎么办
  20. 小饶学编程之JAVA SE第二部分——Web 前端基础:09CSS3

热门文章

  1. 和与余数的和同余理解_每日一题 | 第38期:数量关系之余数特性
  2. 还剩10天,赶紧登下百度网盘,拯救你的2T存储空间吧!
  3. Nginx配置中一个不起眼字符/的巨大作用,失之毫厘谬以千里
  4. 扫码登录是如何实现的?
  5. Nginx 一个牛 X 的功能,流量拷贝!
  6. java流处理为什么快_“任何情况下,都不可以堕胎”是道德普遍主义的观点。
  7. mysql菜鸟教程update_PHP MySQL Update
  8. flink 三种时间机制_360深度实践:Flink 与 Storm 协议级对比
  9. move函数c语言,C++11 move()函数:将左值强制转换为右值
  10. 百度小程序opencard书法字典名家书法等测试日志记录