目录
前言
实现对象的排序,可以考虑两种方法:
- 自然排序(comparable)、定制排序(comparator)
- 内置比较器(Comparable)、外部比较器(Comparator)
一、方式一:实现Comparable接口(自然排序)
1. 定义与注意点
- Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。
- 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小
1. 如果当前对象this大于形参对象obj,则返回正整数,
2. 如果当前对象this小于形参对象obj,则返回负整数,
3. 如果当前对象this等于形参对象obj,则返回零。 - 对于类 C 的每一个 e1 和 e2 来说,当且仅当 e1.compareTo(e2) == 0 与e1.equals(e2) 具有相同的 boolean 值时,类 C 的自然排序才叫做与 equals一致。建议(虽然不是必需的)最好使自然排序与 equals 一致。
2. 实现步骤
- 具体的类A实现Comparable类接口
- 重新Comparable接口中的compareTo(Object o)方法
- 在compareTo方法中指明比较的大小。
返回值为正代表大,为负代表小。一般来说this在前Object O在后是升序,this在后Object O在前是降序。
public class ComparableTest {
@Test
public void test() {
People[] arr = new People[5];
arr[0] = new People("Time",22);
arr[1] = new People("Tom",13);
arr[2] = new People("Jerry",13);
arr[3] = new People("Lucy",19);
arr[4] = new People("Rose",44);
Arrays.sort(arr);
for(People o:arr){
System.out.println(o);
}
/*
People{name='Jerry', age=13}
People{name='Tom', age=13}
People{name='Lucy', age=19}
People{name='Time', age=22}
People{name='Rose', age=44}
*/
}
}
class People implements Comparable {
public String name;
public int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
public People() {
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
if (o == this) {
return 0;
}
if (o instanceof People) {
People p = (People) o;
//判断年纪、升序
int value = Integer.compare(this.age, p.age);
if(0 != value){
return value;
}
//年龄相同的情况,按姓名排序
return this.name.compareTo(p.name);
}
//抛出异常
throw new RuntimeException("类型不一致");
}
}
3. 排序顺序:(默认都是从小到大排列的)
- String:按照字符串中字符的Unicode值进行比较
- Character:按照字符的Unicode值来进行比较
- 数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值大小进行比较
- Boolean:true对应的包装类实例大于 false 对应的包装类实例
- Date、Time等:后面的日期时间比前面的日期时间大
4. 源代码
Comparable 接口仅仅只包括一个函数,它的定义如下:
package java.lang;
import java.util.*;
public interface Comparable {
public int compareTo(T o);
}
解释:
假设我们通过 x.compareTo(y) 来“比较x和y的大小”。
若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。
这里为了大家更好的理解Compareable这个接口, 我自己手写了一个Compareable接口。
5. 演示示例
5.1. 示例1
5.1.1. 分析 Comparable 函数式接口
package java.lang;
import java.util.*;
public interface Comparable {
public int compareTo(T o);
}
5.1.2. Cat 实现 Comparable 接口
public class Cat implements Comparable<Cat> {
private int height;
private int weight;
public Cat(int height, int weight){
this.height = height;
this.weight = weight;
}
public int compareTo(Cat cat){
if(this.height>cat.height){
return 1;
} else if(this.height<cat.height){
return -1;
} else{
return 0;
}
}
@Override
public String toString() {
return "Cat{" +
"height=" + height +
", weight=" + weight +
'}';
}
}
5.1.3. 手写排序算法, 这里用到的是冒泡排序
package com.company.strategy2;
public class Sorter {
public static void sort(Comparable[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int e = arr.length - 1; e > 0; e--) {
for (int i = 0; i < e; i++) {
if (arr[i].compareTo(arr[i + 1]) == 1) {
swap(arr, i, i + 1);
}
}
}
}
public static void swap(Comparable[] arr, int i, int j) {
Comparable temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
5.1.4. 测试类
public class Main {
public static void main(String[] args) {
Cat[] cats = {new Cat(111,111), new Cat(555,555), new Cat(222,222), new Cat(444,444)};
Sorter sorter = new Sorter();
sorter.sort(cats);
System.out.println(Arrays.toString(cats));
}
}
实际使用中则不需要自己手写Comparable接口和sort排序算法
输出结果:
5.2. 示例2
5.2.1. Pig 实现 Comparable 接口
public class Pig implements java.lang.Comparable<Pig> {
private Double weight;
public Pig(Double weight){
this.weight = weight;
}
@Override
public int compareTo(Pig pig) {
if (this.weight>pig.weight) {
return 1;
} else if (this.weight<pig.weight){
return -1;
} else {
return 0;
}
}
@Override
public String toString() {
return "Pig{" +
"weight=" + weight +
'}';
}
}
5.2.2. 测试类
public class Test {
public static void main(String[] args) {
Pig[] pigs = new Pig[]{new Pig(123.5), new Pig(145.6), new Pig(111.6)};
System.out.println(Arrays.toString(pigs));
Arrays.sort(pigs);
System.out.println(Arrays.toString(pigs));
}
}
运行结果:
6. 总结
Comparable接口定义了compareTo方法,用于比较对象,Comparable接口是一个泛型接口。
在实现该接口时,泛型类型E被替换成一种具体的类型。
Java类库中的许多类实现了Comparable接口以定义对象的自然顺序。
Byte、Short、Integer、Long、Float、Double、Character、Biglnteger、BigDecimalxCalendar、String以及
Date类都实现了Comparable接口。
二、方式二:实现Comparator接口(定制排序)
1. 定义与注意点
- 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,
或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序,强行对多个对象进行整体排序的比较 - 重写compare(Object o1,Object o2)方法,比较o1和o2的大小:1. 如果方法返回正整数,则表示o1大于o2;2. 如果返回0,表示相等;3. 如果返回负整数,表示o1小于o2
- 可以将 Comparator 传递给 sort 方法 (如 Collections.sort 或rrrays.sort),从而允许在排序顺序上实现精确控制。
- 还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
2. 使用步骤
- 创建Comparator的一个实例(可以采取匿名的创建方式)。
- 实现Comoarator接口的compare(类A o1, 类A o2)方法。
注意:此时o1相当于comparato(Object o)中的this,o2相当于Object o
- 传入sort方法中
Arrays.sort(arr, new Comparator<People>() {
@Override
public int compare(People o1, People o2) {
if (o1 instanceof People && o2 instanceof People) {
int value = o1.name.compareTo(o2.name);
if (value != 0) {
return value;
} else {
return Integer.compare(o1.age,o2.age);
}
}
throw new RuntimeException("类型不匹配");
}
});
3. 常见API
3.1. reversed()
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
顾名思义,可以知道这个方法是用来生成一个逆序器,比如我们开始需要得到一个正序的排序序列,然后又想得到一
个反转的排序序列,就可以使用该方法。
比如:
Integer [] values = new Integer[]{1,0,3,5,7,8,5,4,3,7,9,56,3,23};
Comparator<Integer> comparator = Comparator.comparingInt(x -> x);
Arrays.sort(values, comparator);
System.out.println(JSON.toJSONString(values));
Arrays.sort(values, comparator.reversed());
System.out.println(JSON.toJSONString(values));
结果如下:
[0,1,3,3,3,4,5,5,7,7,8,9,23,56]
[56,23,9,8,7,7,5,5,4,3,3,3,1,0]
3.2. comparingInt
用法:
static <T> Comparator<T> comparingInt(ToIntFunction <T> keyExtractor)
参数:此方法接受单个参数keyExtractor,该参数是用于提取整数排序键的函数。
返回值:此方法返回一个比较器,该比较器通过提取的键进行比较
异常:如果参数为null,则此方法将引发NullPointerException。
以下示例程序旨在说明compareingInt(java.util.function.ToIntFunction)方法:
示例1:
// Java program to demonstrate
// Comparator.comparingInt(java.util.function.ToIntFunction) method
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class GFG {
public static void main(String[] args)
{
// create some user objects
User u1 = new User("Aaman", 25);
User u2 = new User("Joyita", 22);
User u3 = new User("Suvam", 28);
User u4 = new User("mahafuj", 25);
// before sort
List<User> list
= Arrays.asList(u2, u1, u4, u3);
System.out.println("Before Sort:");
list.forEach(User
-> System.out.println("User age "
+ User.getAge()));
Collections.sort(list,
Comparator.comparingInt(
User::getAge));
System.out.println("\nAfterSort:");
list.forEach(User
-> System.out.println("User age "
+ User.getAge()));
}
}
class User implements Comparable<User> {
public String name;
public int age;
public User(String name, int age)
{
this.name = name;
this.age = age;
}
public int compareTo(User u1)
{
return name.compareTo(u1.name);
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
@Override
public String toString()
{
return "User [name=" + name
+ ", age=" + age + "]";
}
}
4. 演示示例
4.1. 示例1
在这个例子中,我们将会向你展示如何使用java 8的lambda表达式去写一个Comparator,去对List进行排序
4.1.1. 传统的Comparator示例
Comparator<Developer> byName = new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getName().compareTo(o2.getName());
}
};
4.1.2. 等价的lambda表达式写法
Comparator<Developer> byName =
(Developer o1, Developer o2)->o1.getName().compareTo(o2.getName());
4.1.3. 没有Lambda表达式的排序
用Developer对象的年龄进行比较,通常情况下,你会使用Collections.sort,并传递一个匿名的Comparator
package com.zheng.java8;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class TestSorting {
public static void main(String[] args) {
List<Developer> listDevs = getDevelopers();
System.out.println("Before Sort");
for (Developer developer : listDevs) {
System.out.println(developer);
}
//sort by age
Collections.sort(listDevs, new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getAge() - o2.getAge();
}
});
System.out.println("After Sort");
for (Developer developer : listDevs) {
System.out.println(developer);
}
}
private static List<Developer> getDevelopers() {
List<Developer> result = new ArrayList<Developer>();
result.add(new Developer("zheng", new BigDecimal("70000"), 33));
result.add(new Developer("alvin", new BigDecimal("80000"), 20));
result.add(new Developer("jason", new BigDecimal("100000"), 10));
result.add(new Developer("iris", new BigDecimal("170000"), 55));
return result;
}
}
输出:
Before Sort
Developer [name=zheng, salary=70000, age=33]
Developer [name=alvin, salary=80000, age=20]
Developer [name=jason, salary=100000, age=10]
Developer [name=iris, salary=170000, age=55]
After Sort
Developer [name=jason, salary=100000, age=10]
Developer [name=alvin, salary=80000, age=20]
Developer [name=zheng, salary=70000, age=33]
Developer [name=iris, salary=170000, age=55]
当排序要求改变是,我们仅仅只需要替换另一个新的匿名Comparator类
//sort by age
Collections.sort(listDevs, new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getAge() - o2.getAge();
}
});
//sort by name
Collections.sort(listDevs, new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getName().compareTo(o2.getName());
}
});
//sort by salary
Collections.sort(listDevs, new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getSalary().compareTo(o2.getSalary());
}
});
上述代码可以正常运行,但是仅仅因为要修改一行代码,然后就创建一个类,有点奇怪。
4.1.4. Lambda表达式排序
在Java 8中,List支持Sort方法,不在需要去使用Collections.sort。
//List.sort() since Java 8
listDevs.sort(new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o2.getAge() - o1.getAge();
}
});
Lambda表达式示例
package com.zheng.java8;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class TestSorting {
public static void main(String[] args) {
List<Developer> listDevs = getDevelopers();
System.out.println("Before Sort");
for (Developer developer : listDevs) {
System.out.println(developer);
}
System.out.println("After Sort");
//lambda here!
listDevs.sort((Developer o1, Developer o2)->o1.getAge()-o2.getAge());
//java 8 only, lambda also, to print the List
listDevs.forEach((developer)->System.out.println(developer));
}
private static List<Developer> getDevelopers() {
List<Developer> result = new ArrayList<Developer>();
result.add(new Developer("zheng", new BigDecimal("70000"), 33));
result.add(new Developer("alvin", new BigDecimal("80000"), 20));
result.add(new Developer("jason", new BigDecimal("100000"), 10));
result.add(new Developer("iris", new BigDecimal("170000"), 55));
return result;
}
}
输出:
Before Sort
Developer [name=zheng, salary=70000, age=33]
Developer [name=alvin, salary=80000, age=20]
Developer [name=jason, salary=100000, age=10]
Developer [name=iris, salary=170000, age=55]
After Sort
Developer [name=jason, salary=100000, age=10]
Developer [name=alvin, salary=80000, age=20]
Developer [name=zheng, salary=70000, age=33]
Developer [name=iris, salary=170000, age=55]
4.1.5. 更多Lambda表达式的示例
通过年龄排序
//sort by age
Collections.sort(listDevs, new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getAge() - o2.getAge();
}
});
//lambda
listDevs.sort((Developer o1, Developer o2)->o1.getAge()-o2.getAge());
//lambda, valid, parameter type is optional
listDevs.sort((o1, o2)->o1.getAge()-o2.getAge());
通过名称排序
//sort by name
Collections.sort(listDevs, new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getName().compareTo(o2.getName());
}
});
//lambda
listDevs.sort((Developer o1, Developer o2)->o1.getName().compareTo(o2.getName()));
//lambda
listDevs.sort((o1, o2)->o1.getName().compareTo(o2.getName()));
通过薪水排序
//sort by salary
Collections.sort(listDevs, new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getSalary().compareTo(o2.getSalary());
}
});
//lambda
listDevs.sort((Developer o1, Developer o2)->o1.getSalary().compareTo(o2.getSalary()));
//lambda
listDevs.sort((o1, o2)->o1.getSalary().compareTo(o2.getSalary()));
lambda表达式,使用薪水给List排序
Comparator<Developer> salaryComparator = (o1, o2)->o1.getSalary().compareTo(o2.getSalary());
listDevs.sort(salaryComparator);
输出
Developer [name=zheng, salary=70000, age=33]
Developer [name=alvin, salary=80000, age=20]
Developer [name=jason, salary=100000, age=10]
Developer [name=iris, salary=170000, age=55]
Lmabda表达式,使用薪水,倒排List
Comparator<Developer> salaryComparator = (o1, o2)->o1.getSalary().compareTo(o2.getSalary());
listDevs.sort(salaryComparator.reversed());
输出
Developer [name=iris, salary=170000, age=55]
Developer [name=jason, salary=100000, age=10]
Developer [name=alvin, salary=80000, age=20]
Developer [name=zheng, salary=70000, age=33]
三、Comparator & Comparable比较
- Comparable 更像是自然排序
- Comparator 更像是定制排序