关于Comparable、Comparator接口返回值决定顺序的问题

Comparable和Comparator接口都是实现集合中元素的比较、排序的,下面先简单介绍下他们的用法。

1. 使用示例

public class Person {private String name;private Integer age;public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Person o) {return  this.age - o.age;}
}
        ArrayList<Person> list = new ArrayList<>();list.add(new Person("朱一百",100));list.add(new Person("朱八零",80));list.add(new Person("朱八一",81));list.add(new Person("朱九零",90));list.add(new Person("朱八二",82));list.add(new Person("朱七七",77));list.add(new Person("朱八八",88));list.add(new Person("朱重八",88));

1.1 Comparable

public class Person  implements Comparable<Person>{......@Overridepublic int compareTo(Person o) {if (this.getAge() < o.getAge()) {return -1;} else if (this.getAge() > o.getAge()) {return 1;} else {return this.getName().compareTo(o.getName());}}
}
public class ComparableTest {public static void main(String[] args) {......Collections.sort(list);list.forEach(System.out::println);}
}

1.2 Comparator

public class PersonComparator  implements Comparator<Person> {@Overridepublic int compare(Person o1, Person o2) {if (o1.getAge() < o2.getAge()) {return -1;} else if (o1.getAge() > o2.getAge()) {return 1;} else {return o1.getName().compareTo(o2.getName());}}
}
public class ComparatorTest {public static void main(String[] args) {......Collections.sort(list,new PersonComparator());list.forEach(System.out::println);}
}

以上的两个例子输出的结果如下所示:

Person{name='朱七七', age=77}
Person{name='朱八零', age=80}
Person{name='朱八一', age=81}
Person{name='朱八二', age=82}
Person{name='朱八八', age=88}
Person{name='朱啊八', age=88}
Person{name='朱九零', age=90}
Person{name='朱一百', age=100}

按照年龄升序排列,年龄相同时按照姓名升序排序。

2. 关于返回结果-1 0 1 和排序的关系

在上面的例子中我们分别实现了compare()compareTo()对集合继续宁排序,但是我们想知道方法返回-1、0、1对集合排序的关系。

2.1 Comparable

List#sort() =>ArrayList#sort() => Arrays#sort() ⇒ ComparableTimSort#sort() ⇒ ComparableTimSort.#countRunAndMakeAscending()
ArrayList#sort()
可以看到到Comparator c == null 时进入sort() 方法,当我们实现Comparator进行排序时,进入的方法就应该是 TimSort.sort()
Arrays#sort()
ComparableTimSort#sort()
ComparableTimSort.#countRunAndMakeAscending()
countRunAndMakeAscending方法如下:

    private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {assert lo < hi;int runHi = lo + 1;if (runHi == hi)return 1;if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descendingwhile (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)runHi++;reverseRange(a, lo, runHi);} else {                              // Ascendingwhile (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)runHi++;}return runHi - lo;}

如果当前子序列是"递减"的,也就是 a[runHi] 小于 a[lo],那么需要找到连续"递减"的子序列的结束位置 runHi。直到找到第一个不满足"递减"条件的元素。然后,调用 reverseRange 方法将"递减"的子序列反转为"递增"的子序列。

如果当前子序列是"递增"的,也就是 a[runHi] 大于等于 a[lo],那么需要找到连续"递增"的子序列的结束位置 runHi。
最后,返回 runHi - lo,即子序列的长度。

    private static void binarySort(Object[] a, int lo, int hi, int start) {assert lo <= start && start <= hi;if (start == lo)start++;for ( ; start < hi; start++) {Comparable pivot = (Comparable) a[start];int left = lo;int right = start;assert left <= right;/** Invariants:*   pivot >= all in [lo, left).*   pivot <  all in [right, start).*/while (left < right) {int mid = (left + right) >>> 1;// pivot元素的索引比a[mid]的索引大// pivot元素在数组中比a[mid]靠后if (pivot.compareTo(a[mid]) < 0)right = mid;elseleft = mid + 1;}assert left == right;int n = start - left;  // The number of elements to move// Switch is just an optimization for arraycopy in default caseswitch (n) {case 2:  a[left + 2] = a[left + 1];case 1:  a[left + 1] = a[left];break;default: System.arraycopy(a, left, a, left + 1, n);}a[left] = pivot;}}

这个方法做的事情如下:

  1. 经过countRunAndMakeAscending()方法 start位置之前的集合是有序的,于是start位置开始,pivot=array[start],使用二分查找法对start之前已经有序的数组比对,从start下标开始从后往前找,找到start下标之前第一个大于array[start]的元素下标index;
  2. 根据要移动的步数n = start - left做了优化,但是目的是将[left, start-1]范围内的数组值都向后移动一位,放在 [left+1~start],再把pivot的值赋予array[left];
  3. start++,循环执行以上步骤直到数组结束

总结

我们实现的compareTo()方法返回-1 0 1 和集合的排序是升序或者降序没有直接的关系,在上面的方法我们可以看到的是compareTo()返回会导致如下结果:

  1. 返回-1说明两个比较的元素需要交换位置
  2. 但会0或者1不需要交换两个元素的位置

所以我们应该能比较清楚的知道返回-1 0 1 给我们排序带来的效果了,比如上方的pivot.compareTo(a[mid]) pivot在数组中比a[mid]靠后,当 if (this.getAge() < o.getAge()) {return -1}; 我们希望pivot的age小于a[mid]时交换位置,最后排序的结果就是年龄的升序,如果if (this.getAge() < o.getAge()) {return 1};最后排序的结果就是年龄的降序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/101669.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在学习DNS的过程中给我的启发

在国内&#xff0c;关于DNS相关的话题一直络绎不绝&#xff0c;比如DNS根服务器为什么中国没有&#xff0c;还有Anycast BGP实现负载&#xff0c;为什么DNS只有13个&#xff0c;还有DNS over HTTPS 和 DNS over TLS的优劣等等问题&#xff0c;接下来我会找出几个一一说一下其中…

stable diffusion实践操作-常见lora模型介绍

系列文章目录 本文专门开一节写Lora相关的内容&#xff0c;在看之前&#xff0c;可以同步关注&#xff1a; stable diffusion实践操作 文章目录 系列文章目录前言一、什么是lora?1.1 lora 定义1.2 lora的基本原理1.2 通过分层控制lora 二、作用&#xff1a;2.1 复刻人物特征2…

教育培训小程序的设计与功能解析

随着互联网的发展&#xff0c;线上教育逐渐成为一种趋势&#xff0c;越来越多的人开始选择在线学习。而搭建一个适合自己的线上教育小程序&#xff0c;可以为教育机构或个人提供更好的教学和学习体验。在本文中&#xff0c;我们将介绍如何通过一个第三方制作平台来搭建在线教育…

【FPGA零基础学习之旅#12】三线制数码管驱动(74HC595)串行移位寄存器驱动

&#x1f389;欢迎来到FPGA专栏~三线制数码管驱动 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能指…

IBM Spectrum LSF Explorer 为要求苛刻的分布式和任务关键型高性能技术计算环境提供强大的工作负载管理

IBM Spectrum LSF Explorer 适用于 IBM Spectrum LSF 集群的强大、轻量级报告解决方案 亮点 ● 允许不同的业务和技术用户使用单一解决方案快速创建和查看报表和仪表板 ● 利用可扩展的库提供预构建的报告 ● 自定义并生成性能、工作负载和资源使用情况的报…

springboot初试elasticsearch

引入依赖 elasticsearch的依赖版本与你elasticsearch要一致 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency> 索引库的操作 创建索引库 impo…

跳槽面试:如何转换工作场所而不失去优势

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

c++ noexcept

引入noexcept原因&#xff1a; 异常规范的检查是在运行期而不是编译期&#xff0c;因此程序员不能保证所有异常都得到了 catch 处理。由于第一点的存在&#xff0c;编译器需要生成额外的代码&#xff0c;在一定程度上妨碍了优化。模板函数中无法使用。赋值函数、拷贝构造函数和…

小红书笔记爬虫

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

【网络安全带你练爬虫-100练】第20练:数据处理-并写入到指定文档位置

目录 一、目标1&#xff1a;解码去标签 二、目标2&#xff1a;提取标签内内容 三、目标3&#xff1a;处理后的数据插入原位置 四、目标4&#xff1a;将指定的内容插入指定的位置 五、目标5&#xff1a;设置上下文字体格式 六、目标6&#xff1a;向多个不同位置插入不同的…

ClickHouse 存算分离改造:小红书自研云原生数据仓库实践

ClickHouse 作为业界性能最强大的 OLAP 系统&#xff0c;在小红书内部被广泛应用于广告、社区、直播和电商等多个业务领域。然而&#xff0c;原生 ClickHouse 的 MPP 架构在运维成本、弹性扩展和故障恢复方面存在较大局限性。为应对挑战&#xff0c;小红书数据流团队基于开源 C…

嵌入式开发-11 Linux下GDB调试工具

目录 1 GDB简介 2 GDB基本命令 3 GDB调试程序 1 GDB简介 GDB是GNU开源组织发布的一个强大的Linux下的程序调试工具。 一般来说&#xff0c;GDB主要帮助你完成下面四个方面的功能&#xff1a; 1、启动你的程序&#xff0c;可以按照你的自定义的要求随心所欲的运行程序&#…