Java 对象排序完整版
前几天在 LeetCode 刷题的时候,遇到了利用 Arrays.sort() 或 Collections.sort() 来对 Java 对象进行排序的需求,于是想较详细地总结一下 Java 对象的排序实现方法,这些方法能让我们的编程更快捷。
在 Java 中,基本使用两种方法,即 Comparator 和 Comparable 接口,来完成基本甚至稍微复杂一点的排序的任务。当然,面对不同的数据结构,如数组(Array)、集合(Set)或映射(Map),排序的实现方式稍有不同。
要排序的对象
本文的任务就是对一系列的 Person 类对象进行排序,其中 Person 类的定义如下,
/** 原始 Person 类,每个对象包含 name 人名和 age 年龄属性 */
class Person {String name; // 人名int age; // 年龄public Person(String n, int a) {this.name = n;this.age = a;}/** 重写 Object 的 toString() 方法,便于打印对象 */@Overridepublic String toString() {return ("name:" + this.name + ", age:" + this.age).toString();}
}
方法1: 使用 Comparable 接口
Comparble 接口的使用方法很直观,在要比较的对象的类的声明中实现 Comparable 接口即可。例如我们想要对 Person 对象进行排序,首先在 Person 类的声明中加上 implements Comparable<Person>
的语句,然后重写 Comparable 接口的 compareTo 的方法,按照比较规则返回 1,-1 或者 0。示例代码如下,
/** 实现了 Comparable 接口的 Person 类,* 每个 Person 对象都是可比较的,比较的依据是 age 属性。* */
class Person implements Comparable<Person>{String name; // 人名int age; // 年龄public Person(String n, int a){this.name = n;this.age = a;}/** 重写 Comparable 的 compareTo 方法,表示按照人的年龄来排序 */@Overridepublic int compareTo(Person p2){if(this.age > p2.age){return 1;}else if(this.age < p2.age){return -1;}else{return 0;}}/** 重写 Object 的 toString() 方法,便于打印对象 */@Overridepublic String toString(){return ("name:" + this.name + ", age:" + this.age).toString();}
}
在声明了上述的 Person 对象后,从根本上让 Person 对象具有了“可比较”的特性,我们直接利用 Arrays.sort() 或者 Collections.sort() 来比较一系列的 Person 对象了。示例代码如下,
import java.util.Arrays;public class HelloWorld{public static void main(String []args){// 构建 Person 对象数组,包含 5 个对象Person[] ps = new Person[5];ps[0] = new Person("John", 30);ps[1] = new Person("Mike", 22);ps[2] = new Person("Kim", 34);ps[3] = new Person("Canddy", 25);ps[4] = new Person("Sam", 28);// 利用 sort 方法对 Person 数组进行排序Arrays.sort(ps);// 打印 Person 数组for(int i=0; i<ps.length; i++){System.out.println(ps[i]);}}
}
执行上述代码可以得到新的数组,我们打印这个数组,其输出确实是按照人的年龄排序的。而且 sort() 函数的排序效率较高,因此可以把排序数组放心的交给它。
这里插一句题外话,根据 Java 8 源码可知,Arrays.sort() 函数内部使用的排序算法是一种包含多种基本排序算法的混合算法1。
在 Java 实现中,Arrays.sort()(主要针对数组)或 Collections.sort()(主要针对列表)底层使用了多种算法,大概的思路为,
1 - 当元素个数小于 47 时,使用 插入排序算法(时间复杂度 O(n^2));
2 - 当元素个数大于等于 47 且小于 286 时,使用 快速排序算法(时间复杂度 O(n×\times×log(n)));
3 - 当数组个数大于等于 286 时,使用 归并排序算法(时间复杂度O(n×\times×log(n)),空间复杂度 O(n))。
方法 2:使用 Comparator 接口
上一节的比较对象的方式需要我们让对象类实现 Comparable 接口并重写 compareTo 方法,但当我们没有办法改变对象类的实现,只能靠外部来对对象进行排序时,就只能使用 Comparator 接口了。
在编程中,Comparator 接口一般被匿名继承并作为第二个参数传递给 Arrays.sort() 函数。按照上一节的例子,假设我们不能对 Person 类进行修改(使用原始的 Person 类),我们可以使用如下代码对其对象数组进行排序。
import java.util.Arrays;
import java.util.Comparator;public class HelloWorld{public static void main(String []args){// 构建 Person 对象数组,包含 5 个对象Person[] ps = new Person[5];ps[0] = new Person("John", 30);ps[1] = new Person("Mike", 22);ps[2] = new Person("Kim", 34);ps[3] = new Person("Canddy", 25);ps[4] = new Person("Sam", 28);// 利用 sort 方法对 Person 数组进行排序Arrays.sort(ps, new Comparator<Person>(){/** 重写 compare 方法来对 Person 对象进行比较 */@Overridepublic int compare(Person p1, Person p2){if(p1.age > p2.age){return 1;}else if(p1.age < p2.age){return -1;}else{return 0;}}});// 打印 Person 数组for(int i=0; i<ps.length; i++){System.out.println(ps[i]);}}
}
可见比较的结果与使用 Comparable 接口的方法相同。
由于 Comparator 是函数式接口(只有一个抽象函数的接口),我们也可以使用 Lambda 表达式来让代码更简洁,此时只需要把核心代码改为,
// 利用 lambda 表达式来简化代码
Arrays.sort(ps, (Person p1, Person p2) -> {if(p1.age > p2.age) return 1;else if(p1.age < p2.age) return -1;else return 0;
});
这里插一句题外话,如果比较的是字符串,Java 提供了一个很好的比较方法,即 compareTo(String s)
,该方法能自动返回 2 个字符串大小比较的结果,比如说我们此处按照人名来排列,那么代码应该为,
Arrays.sort(ps, (Person p1, Person p2) -> {return p1.name.compareTo(p2.name);
});
Java 在实现 String 类(字符串类)的时候,为了方便字符串比较,也让 String 类实现了 comparable 接口,因此每个 String 对象均有 compareTo() 方法。该方法会从 2 个字符串首个字母开始,依次比较对应位置上的字符的 ASCII 码值,值越大的字符串更大2 。如 “aba”,“abb”,“abab” 的排序结果是 “aba”, “abab”, “abb”。
集合(Set)对象的排序
当 Person 对象出现在集合中时,如 HashSet 中时,我们是无法对集合中的元素进行排序的,这时候我们应该思考使用可替代的数据结构。这里给出使用 TreeSet 来对元素进行排序的实例,
import java.util.Set;
import java.util.TreeSet;
import java.util.Comparator;public class HelloWorld{public static void main(String []args){// 构建 Person 对象集合,包含 5 个对象Set<Person> psSet = new TreeSet<Person>(new PersonComparator());psSet.add(new Person("John", 30));psSet.add(new Person("Mike", 22));psSet.add(new Person("Kim", 34));psSet.add(new Person("Canddy", 25));psSet.add(new Person("Sam", 28));// 打印 Person 数组for(int i=0; i<psList.size(); i++){System.out.println(psList.get(i));}}
}
/** Person 类的比较器 */
class PersonComparator implements Comparator<Person>{@Overridepublic int compare(Person p1, Person p2) {if(p1.age > p2.age){return 1;}else if(p1.age < p2.age){return -1;}else {return 0;}}
}
TreeSet 的元素对象类继承 Compable 接口或提供一个继承 Comparator 的比较器。类似的,你也可以让 Person 类实现 Comparable 接口来让 TreeSet 中的元素变得有序。
映射(Map)对象的排序
Java 中的映射是一个 key-value 的键值对,我们既可以按照 key 排序,也可以按照 value 来排序。如果我们使用的是 TreeMap,那么该映射会自动按照 key 值进行排序;如果我们使用的是 HashMap,那么该映射的输出顺序是不定的。
但是无论针对哪一种映射结构,我们对其中的元素进行排序的方法都是固定的,也就是首先将映射中的所有 Entry 单元存放到一个 ArrayList 列表中去,然后对这个 ArrayList 进行排序。具体的实例代码如下,
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;public class HelloWorld{public static void main(String[] argv) {// 映射中初始有 5 个元素,key 为姓名,value 为年龄Map<String, Integer> psMap = new HashMap<String, Integer>();psMap.put("John", 30);psMap.put("Mike", 22);psMap.put("Kim", 34);psMap.put("Canddy", 25);psMap.put("Sam", 28);// 将映射中的 Entry 对象放入 ArrayList 中并排序List<Map.Entry<String, Integer>> list = new ArrayList<>(psMap.entrySet());Collections.sort(list, (Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) -> {if(e1.getValue() > e2.getValue()) return 1;else if(e1.getValue() < e2.getValue()) return -1;else return 0;});// 打印映射中的元素for(int i=0; i<list.size(); i++) {System.out.println(list.get(i).getKey() + ": " + list.get(i).getValue());}}
}
在 java 中,映射的底层实现是数组加链表(或红黑树)3,想要原地对映射进行排序是很困难的。因此,将映射中的 Entry 对象放入列表中排序只是一个权宜之计。另外,也建议大家使用 Entryset() 来遍历映射,因为它比使用键值来遍历(即 Keyset())更加高效。
小总
Java 对象的排序思路主要有 Comparable 和 Comparator 接口两种办法。前者让对象本身具有“可比较”的属性,即让对象类继承 Comparable 接口,重写 compareTo 方法;后者创建一个继承 Comparator 接口的比较器 XXCompartor,重写 compare 方法,最后将该比较器作为参数传递给 Arrays.sort() 或 Collection.sort() 方法。
针对不同数据结构中的对象,我们可以灵活设计排序方法。对于数组,我们使用 ArrayList.sort();对于列表,我们使用 Collections.sort();对于集合,我们使用 TreeSet 的数据结构;对于映射,我们针对每个 Entry 对象进行排序。
参考:
Michael孟良, “剖析JDK8中Arrays.sort底层原理及其排序算法的选择”. Link ↩︎
Kevin_cai09, “浅谈string中的compareTo方法”. Link ↩︎
清浅池塘, “HashMap底层实现原理”. Link ↩︎
Java 对象排序完整版相关推荐
- Java对象排序、中文排序、SortedSet排序使用和源码讲解
在C.C++中有很多排序算法,但是通常排序算法不得不让程序员在写代码的过程中陷入对底层很多指针和位置的理解,Java不希望这样,所以排序大多可以由java帮你做掉,例如,你要对一个数组排序,就通过:C ...
- java对象排序_Java™ 教程(对象排序)
对象排序 List l可以如下排序. Collections.sort(l); 如果List包含String元素,它将按字母顺序排序,如果它由Date元素组成,它将按时间顺序排序,这是怎么发生的?St ...
- Java 对象排序 Comparable接口和比较器Comparator的使用
我这里使用的是Treeset存储的学生信息,定义了学生类,对学生信息进行一个排序: 方法一 对象排序的的方法是,让元素所属类实现 Comparable 接口 ,重写 comparaTo(To) 方法 ...
- java对象排序_java对象排序(Comparable)详细实例
对象实现Comparable接口 package collections.sort.comparable; public class Field implements Comparable{ priv ...
- Java 对象排序详解
很难想象有Java开发人员不曾使用过Collection框架.在Collection框架中,主要使用的类是来自List接口中的ArrayList,以及来自Set接口的HashSet.TreeSet,我 ...
- java教程pdf(java教程视频完整版)
JAVA程序设计基础教程PDF 你在百度文库里找找好像有的,如果没有就去taobao吧,太多的二手旧书了.百度 谷歌上面这样的只是多的是 还有就是百度百科fsd <JAVA从入门到精通>电 ...
- 达内第二个月考java核心题目,(完整版)达内科技JAVA项目经理入职考试--I(不含答案)--新...
达内科技项目经理入职考试 (JAVA方向 A卷) 考试说明: 本考试为达内科技JAVA产品线项目经理入职考试,考试使用闭卷.笔试的形式.满分100分,考试时间120分钟.考试期间不允许使用电脑,不可打 ...
- 你真的了解Java系统启动流程吗?java基础教程完整版
摘要 Apache Kafka是一个分布式消息发布订阅系统.它最初由LinkedIn公司基于独特的设计实现为一个分布式的提交日志系统( a distributed commit log),之后成为Ap ...
- 黑马JAVA P85-87 ATM完整版
package com.itheima;import java.sql.SQLOutput; import java.util.ArrayList; import java.util.Random; ...
最新文章
- PYTHON2.day07
- 将unicode编码的txt文件转为utf-8编码
- android浮动文本,android – 如何将文本添加到浮动操作按钮?
- 电商网站数据分析的重要性
- CentOS7.5下时间戳转换为时间
- pytorch functional
- shell脚本if条件总结
- bitmap转换为drawable
- bash中正则表达式
- React Native运行原理解析
- End-to-end Recovery of Human Shape and Pose
- The Reactive Manifesto(响应式宣言)
- 2021-09-0818. 四数之和
- 交互式计算机图形学 第六版 pdf,1交互式计算机图形学系统.pdf
- Linux Bind 安装
- 什么是长元音和短元音
- eNSP中静态路由器和默认路由器的配置连接
- 参会记录|全国多媒体取证暨第二届多媒体智能安全学术研讨会(MAS‘2023)
- 复杂网络分析总结[Network Analysis]
- 一. 知识图谱和金融领域简述