Comparator比较器

目录

1. 用处

2. 规范

3. 比较器功能方法

3.1. int compare(T o1,T o2)

3.2. boolean equals(Object obj)

4. 比较器的使用

4.1. Stream.sorted

4.2. Collections.sort

4.3. List.sort

5. 常见方法

5.1. reversed()

5.2. thenComparing()

5.3. <静态>reverseOrder()

5.4. <静态>naturalOrder()

5.5. <静态>nullsFirst()

5.6. <静态>nullsLast()

5.7. <静态>comparing()


1. 用处

用于对对象集合进行总排序:

  1. 传递给排序方法(如 Collections.sort / Arrays.sort),以便精确控制排序顺序。
  2. 用于控制某些数据结构的排序。
  3. 为没有 natural ordering 的对象集合提供排序。

2. 规范

  1. 实现此接口的对象列表可以通过Collections.sortArrays.sort 自动排序。无序指定比较器。
  2. 当且仅当类 C 的 e1 和 e2 的 e1.compareTo(e2) == 0 的布尔值与 e1.equals(e2) 的布尔值相同时,类 C 的自然排序被认为与等价一致。
  3. 强烈建议 a.equals(b)a.compareTo(b) == 0的逻辑相同,否则将会发生奇怪的错误。
  4. 如果将比较器作用于一个可序列化的数据结构,如 TreeSetTreeMap,则实现比较器接口时也需要实现 Serializable 接口进行序列化。

3. 比较器功能方法

3.1. int compare(T o1,T o2)

用来比较两个参数的大小:当 o1 小于、等于、大于 o2 时,分别返回负值、零、正值。

其实现方式有两种:使用 lambda 表达式定义、自定义 compare 实现比较器。

实现一:使用 lamdba 表达式

定义方式:

// 通过Student的age属性进行比较
Comparator<Student> ageComp =(s1 , s2) -> s1.getAge() - s2.getAge();
// 通过Student的name属性进行比较
Comparator<Student> nameComp = (s1 , s2) -> s1.getName() - s2.getName();

使用示例:

import java.util.Comparator;
import java.util.List;
public class CompareDemo {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	list.sort(ageComp);
	list.forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	list.sort(nameComp);
	list.forEach(s -> System.out.println(s));	
  }
} 

实现二:自定义 compare 实现比较器

定义方式:

class AgeComparator implements Comparator<Strudent> , Serializable{
    private static final long serialVersionUID = 1L;
    @Override
    public int compare(Student s1, Student s2){
        return s1.getAge - s2.getAge;
    }
}

3.2. boolean equals(Object obj)

重写了 Object 的 equals 方法,用于比较两个比较器是否相等;

当 obj 也是一个比较器,并且他的排序于此比较器相同时,才会返回 true。也就是说,comp1.equals(comp2)意味着对于任意的 o1,o2 都有 sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2)) ,其中的 sgn 是 java 中的符号函数,将正数返回 1,负数返回 -1,0 依然返回 0;

4. 比较器的使用

可以将比较器与 Stream.sorted/Collections.sort/List.sort/Arrays.sort方法相结合来使用。

4.1. Stream.sorted

Stream.sorted 返回一个由这个流的元素组成的流,根据提供的比较器进行排序。

import java.util.Comparator;
import java.util.List;
public class CompareDemoStreamSorted {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	list.stream().sorted(ageComp).forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	list.stream().sorted(nameComp).forEach(s -> System.out.println(s));	
  }
} 

4.2. Collections.sort

Collections.sort 根据给定的比较器实例对指定的列表进行排序。

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CompareDemoCollectionsSort {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	Collections.sort(list, ageComp);
	list.forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	Collections.sort(list, nameComp);
	list.forEach(s -> System.out.println(s));	
  }
} 

4.3. List.sort

List.sort 根据给定的比较器实例对这个列表进行排序。

import java.util.Comparator;
import java.util.List;
public class CompareDemoListSort {
  public static void main(String[] args) {
	List<Student> list = Student.getStudentList();
	
	System.out.println("--- Sort Students by age ---");
	
	Comparator<Student> ageComp = (s1, s2) -> s1.getAge() - s2.getAge();
	list.sort(ageComp);
	list.forEach(s -> System.out.println(s));
	
	System.out.println("--- Sort Students by name ---");
	
	Comparator<Student> nameComp = (s1, s2) -> s1.getName().compareTo(s2.getName());	
	list.sort(nameComp);
	list.forEach(s -> System.out.println(s));	
  }
} 

5. 常见方法

5.1. reversed()

  • 返回一个比较器,该比较器的排序与此比较器相反。

5.2. thenComparing()

当两个对象通过主排序规则比较结果相等时,thenComparing 方法会提供一个次级排序规则来进一步比较。这种链式调用可以实现多级排序(先按照第一个规则排序,如果相等再按照第二个规则排序)

thenComparing 的重载方法:

  1. thenComparing(Comparator<? super T> other)
    1. 接受另一个 Comparator 作为次级排序规则。
  1. thenComparing(Function<? super T, ? extends U> keyExtractor)
    1. 接受一个函数,从对象中提取键值,然后使用自然排序。
  1. thenComparing(Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator)
    1. 接受一个函数和一个比较器,先提取键值,再使用指定的比较器进行排序。
  1. thenComparingInt(ToIntFunction<? super T> keyExtractor)
    1. 专门用于比较整数键值,效率更高。 ‘

代码示例:

// Person类
class Person {
    String name;
    int age;
    double height;

    public Person(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + ", height=" + height + '}';
    }
}

//示例
import java.util.*;
import java.util.function.Function;

public class Main {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 30, 5.4),
            new Person("Bob", 25, 5.8),
            new Person("Charlie", 30, 5.4),
            new Person("David", 30, 5.9),
            new Person("Eve", 25, 5.8)
        );

        // 多级排序:先按年龄,再按身高,再按姓名
        Comparator<Person> comparator = Comparator
            .comparingInt((Person p) -> p.age) // 主排序:按年龄升序
            .thenComparingDouble(p -> p.height) // 次排序:按身高升序
            .thenComparing(p -> p.name); // 再次排序:按姓名字典序

        // 排序
        people.sort(comparator);

        // 输出结果
        people.forEach(System.out::println);
    }
}

重载方法 3 的代码示例:

    class Product {
        String name;
        double price;
        double rating;

        public Product(String name, double price, double rating) {
            this.name = name;
            this.price = price;
            this.rating = rating;
        }

        @Override
        public String toString() {
            return "Product{name='" + name + "', price=" + price + ", rating=" + rating + '}';
        }
    }

    @Test
    public void test(){
        List<Product> products = Arrays.asList(
                new Product("Laptop", 1200.0, 4.5),
                new Product("Phone", 800.0, 4.8),
                new Product("Tablet", 800.0, 4.4),
                new Product("Monitor", 300.0, 4.8),
                new Product("Keyboard", 300.0, 4.5),
                new Product("Mouse", 300.0, 4.5)
        );

        // 定义排序规则
        Comparator<Product> comparator = Comparator
                // 主排序规则:按价格升序
                .comparingDouble((Product p) -> p.price)
                // 次排序规则:按评分降序
                .thenComparing(
                        (Product p) -> p.rating,
                        (rating1, rating2) -> Double.compare(rating2, rating1)
                )
                // 再次排序规则:按名称字典序升序
                .thenComparing(p -> p.name);

        // 对商品列表排序
        products.sort(comparator);

        // 输出排序后的结果
        products.forEach(System.out::println);
    }

5.3. <静态>reverseOrder()

返回一个比较器,这个比较器的排序规则是自然排序的逆序。

5.4. <静态>naturalOrder()

返回一个比较器,这个比较器的排序规则是自然排序。

5.5. <静态>nullsFirst()

返回一个对空值友好的比较器,她会认为 null 小于非空值,即排序后 null 值处于列表的最前方。

5.6. <静态>nullsLast()

返回一个对空值友好的比较器,她会认为 null 大于非空值,即排序后 null 值处于列表的最后方。

5.7. <静态>comparing()

  1. comparing(Function<? super T,? extends U> keyExtractor)

接受一个函数,该函数从类型 T 中提取 Comparable 排序键,并返回一个按该排序键进行比较的 Comparator<T>.

eg:

Comparator<Student> nameComparator = Comparator.comparing(Student::getName); 
/**
这个排序器用于Student类,用于按照name的字典序排序。
*/

  1. comparing(Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator)

接受一个从类型 T 中提取排序键的函数,并返回一个使用指定 Comparator 按该排序键进行比较的 Comparator<T>.

  1. comparingDouble(ToDoubleFunction<? super T> keyExtractor)

作用同 1,不过要求提取出的排序键必须是 double 类型。

  1. comparingInt(ToIntFunction<? super T> keyExtractor)

作用同 1,不过要求提取出的排序键必须是 int 类型。

  1. comparingLong(ToLongFunction<? super T> keyExtractor)

作用同 1,不过要求提取出的排序键必须是 Long 类型。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值