一、Arrays.sort()与Collections.sort()排序
1️⃣Arrays.sort() 可以对数组,字符串等排序:
import java.util.Arrays;
public class sort {
public static void main(String[] args) {
int[] num = new int[]{3,2,4,1,5};
Arrays.sort(num);
for(int i=0;i
2️⃣Collections.sort() 是对 list 集合排序,list 也可以放数字、字符串:
import java.util.ArrayList;
import java.util.Collections;
public class sort {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add(3);
list.add(2);
list.add(1);
Collections.sort(list);
System.out.print(list);
}
}
//[1, 2, 3]
3️⃣一般没有特殊要求时,直接调用(底层默认的升序排列)就可以得到想要的结果。所谓的 sort 方法排序底层都是基于这两种排序,故如果需要设计成所想要的排序就需要了解底层排序原理。对自定义对象数组排序,需要引入“比较器”的概念。Compareable 和 Compartor 接口就是比较器抽象接口,通过实现类重写接口方法来进行对象比较。
二、Compareable 接口分析
1️⃣当使用 sort(Objetc[] a) 来进行对象的自然排序时,该对象必需实现 Compareable 接口,重写 compareableTo 方法,并一般在此方法中定义 3 种返回值(正,零,负)来进行排序标准的确认,一般默认下面值来表示:
- return 1 时,按照升序。
- return 0 时,原位置不动。
- return -1 时,按照降序。
而让对象继成 Compareable 接口的方式,称为内部比较器。
2️⃣应用:让需要进行排序的对象的类实现 Comparable 接口,重写 compareTo(),在其中定义排序规则,那么就可以直接调用 Collections.sort() 来排序对象数组。
@Data
public class Student implements Comparable{
private int id;
private int age;
private int height;
private String name;
public Student(int id, String name, int age, int height) {
this.id = id;
this.name = name;
this.age = age;
this.height = height;
}
@Override
public int compareTo(Object o) {
Student s = (Student) o;
if (this.age > s.age) {
return 1;
}else if (this.age < s.age) {
return -1;
}else {
if (this.height >= s.height) {
return 1;
}else {
return -1;
}
}
}
}
上面是实现升序排列,如果要实现降序只需要把 1 与 -1 互换位置即可。测试类:
public class Test {
public static void printData(List list) {
for (Student student : list) {
System.out.println("学号:"+student.getId()+"姓名:"+student.getName()+
"年龄"+student.getAge()+"身高:"+student.getHeight());
}
}
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(new Student(1, "A", 20, 180));
list.add(new Student(2, "B", 21, 175));
list.add(new Student(3, "C", 22, 190));
list.add(new Student(4, "D", 21, 170));
list.add(new Student(5, "E", 20, 185));
System.out.println("before sorted");
printData(list);
Collections.sort(list);
System.out.println("after age and height sorted");
printData(list);
}
}
结果:
before sorted
学号:1姓名:A年龄20身高:180
学号:2姓名:B年龄21身高:175
学号:3姓名:C年龄22身高:190
学号:4姓名:D年龄21身高:170
学号:5姓名:E年龄20身高:185
after age and height sorted
学号:1姓名:A年龄20身高:180
学号:5姓名:E年龄20身高:185
学号:4姓名:D年龄21身高:170
学号:2姓名:B年龄21身高:175
学号:3姓名:C年龄22身高:190
三、Compartor 接口分析
1️⃣①先建一个基本属性类:
@Data
public class Student {
//创建两个基本属性
String name="";
int age=0;
//重写构造方法用来传递数据
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
创建按照姓名升序排列的实现类 :
import java.util.Comparator;
//按照名字的升序排列 实现接口 泛型是自定义类,里面有要排序的内容
public class NameSort implements Comparator{
@Override //两个参数是泛型的对象
public int compare(Student o1, Student o2) {
//按照姓名的升序排列,前面加个负号就按照降序排列
return o1.getName().compareTo(o2.getName());
}
}
②直接定义Java中PriorityQueue优先级队列就是利用这原理
:
Arrays.sort(num,new Comparator(){
//传进来的数组或是字符串以及集合list
@Override
public int compare(String o1,String o2){
return o1.compareTo(o2);//升
//return o2.compareTo(o1);//降
}
});
2️⃣应用:实现比较器接口 Comparator,重写 compare 方法,直接当做参数传进 sort 中。
@Data
public class Student {
private int id;
private int age;
private int height;
private String name;
public Student(int id, String name, int age, int height) {
this.id = id;
this.name = name;
this.age = age;
this.height = height;
}
}
测试类:
public class Test {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(new Student(1, "A", 20, 180));
list.add(new Student(2, "B", 21, 175));
list.add(new Student(3, "C", 22, 190));
list.add(new Student(4, "D", 21, 170));
list.add(new Student(5, "E", 20, 185));
System.out.println("before sorted");
printData(list);
Collections.sort(list, new Comparator() {
@Override
public int compare(Student o1, Student o2) {
if(o1.getAge() >= o2.getAge()) {
return 1;
}
else {
return -1;
}
}
});
System.out.println("after age sorted");
printData(list);
Collections.sort(list, new Comparator() {
@Override
public int compare(Student o1, Student o2) {
if(o1.getAge() > o2.getAge()) {
return 1;
}else if (o1.getAge() < o2.getAge()){
return -1;
}else {
if (o1.getHeight() >= o2.getHeight()) {
return 1;
}else {
return -1;
}
}
}
});
System.out.println("after age and height sorted");
printData(list);
}
public static void printData(List list) {
for (Student student : list) {
System.out.println("学号:"+student.getId()+"姓名:"+student.getName()+
"年龄"+student.getAge()+"身高:"+student.getHeight());
}
}
}
输出结果:
before sorted
学号:1姓名:A年龄20身高:180
学号:2姓名:B年龄21身高:175
学号:3姓名:C年龄22身高:190
学号:4姓名:D年龄21身高:170
学号:5姓名:E年龄20身高:185
after age sorted
学号:1姓名:A年龄20身高:180
学号:5姓名:E年龄20身高:185
学号:2姓名:B年龄21身高:175
学号:4姓名:D年龄21身高:170
学号:3姓名:C年龄22身高:190
after age and height sorted
学号:1姓名:A年龄20身高:180
学号:5姓名:E年龄20身高:185
学号:4姓名:D年龄21身高:170
学号:2姓名:B年龄21身高:175
学号:3姓名:C年龄22身高:190
单从上面的例子可以看出排序是稳定的,查看 Java 的Collections.sort
的源代码,确实是基于稳定的归并排序实现的,内部还做了优化,叫TimSort。
3️⃣API排序
public static void main(String[] args) {
List stuList = new ArrayList<>();
stuList.add(new Student(1, "A", 20, 180));
stuList.add(new Student(2, "B", 21, 175));
stuList.add(new Student(3, "C", 22, 190));
stuList.add(new Student(4, "D", 21, 170));
stuList.add(new Student(5, "E", 20, 185));
System.out.println(stuList);
System.out.println("-------------先年龄升---后身高升-------------");
Comparator compareAge = Comparator.comparing(Student::getAge);
Comparator compareHight = Comparator.comparing(Student::getHeight);
Collections.sort(stuList, compareAge.thenComparing(compareHight));
for (Student s : stuList) {
System.out.println(s.getName() + "," + s.getAge() + "," + s.getHeight());
}
System.out.println("-------------先年龄升---后身高降-------------");
Collections.sort(stuList, compareAge.thenComparing(compareHight.reversed()));
for (Student s : stuList) {
System.out.println(s.getName() + "," + s.getAge() + "," + s.getHeight());
}
System.out.println("-------------先年龄降---后身高降-------------");
Collections.sort(stuList, compareAge.reversed().thenComparing(compareHight.reversed()));
for (Student s : stuList) {
System.out.println(s.getName() + "," + s.getAge() + "," + s.getHeight());
}
}
输出结果:
[Student(id=1, age=20, height=180, name=A), Student(id=2, age=21, height=175, name=B), Student(id=3, age=22, height=190, name=C), Student(id=4, age=21, height=170, name=D), Student(id=5, age=20, height=185, name=E)]
-------------先年龄升---后身高升-------------
A,20,180
E,20,185
D,21,170
B,21,175
C,22,190
-------------先年龄升---后身高降-------------
E,20,185
A,20,180
B,21,175
D,21,170
C,22,190
-------------先年龄降---后身高降-------------
C,22,190
B,21,175
D,21,170
E,20,185
A,20,180
Process finished with exit code 0
四、compareTo 源码说明
1️⃣String 比较用 compareTo 方法,针对参与比较的不同的字符,从第一位开始往后比较,然后返回相应的 int 值:
- 字符个数相同,返回参与比较的前后两个字符串的 ASSIC 码差值。
- 两个字符串首字母不同,则该方法返回首字母的 ASSIC 码差值。
- 参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的 ASSIC 码差值。
- 两个字符串不一样长,可以参与比较的字符又完全一样,则返回两个字符串的长度差值。
- 目前 compareTo 项目中的用途是比较版本号的高低。
private final char value[];
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
2️⃣Java 中的 compareto 方法实例
public static void main(String[] args) {
//返回参与比较的前后两个字符串的ASSIC码的差值
String a = "a";//97
String b = "b";//98
System.out.println("a.compareTo(b):" + a.compareTo(b));//-1
System.out.println("b.compareTo(a):" + b.compareTo(a));//1
String c = "c";
String c1 = "c";
System.out.println("c.compareTo(c1):" + c.compareTo(c1));//0
//两个字符串首字母不同,则该方法返回首字母的ASSIC码的差值
String d = "abc";
String e = "bcdfg";
System.out.println("d.compareTo(e):" + d.compareTo(e));//-1
//参与比较的两个字符串如果首字符相同,则比较下一个字符,
//直到有不同的为止,返回该不同的字符的ASSIC码差值
String g = "abedfg";
System.out.println("d.compareTo(g):" + d.compareTo(g));//-2
//两个字符串不一样长,可以参与比较的字符又完全一样,
//则返回两个字符串的长度差值
String h = "abcdefg";
System.out.println("d.compareTo(h):" + d.compareTo(h));//-4
String i = "ab";
System.out.println("d.compareTo(i):" + d.compareTo(i));//1
//目前compareTo项目中的用途是比较版本号的高低
String num = "1.0.0";
String val = "1.0.1";
System.out.println("num.compareTo(val):" + num.compareTo(val));//-1
}
结果如下:
3️⃣面试:
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
public class Solution {
public String PrintMinNumber(int [] numbers) {
if(numbers.length==0) {
return "";
}
int len = numbers.length;
String[] str = new String[len];
StringBuilder sb = new StringBuilder();
for(int i=0;i(){
@Override
public int compare(String o1,String o2){
String c1 = o1 + o2;
String c2 = o2 + o1;
return c1.compareTo(c2);
}
});
for(int i=0;i