🌝🌑🌒🌓🌔�|

黑心老魔

园龄:2年1个月粉丝:0关注:0

2024-11-04 20:01阅读: 238评论: 0推荐: 0

Java比较器 Comparator 在排序中的应用

Java比较器 Comparator 在排序中的应用

基于IJava编辑

在计算机科学中,排序比较是数据处理的核心操作之一。无论是对数据进行排序、查找还是过滤,都需要一种机制来确定元素之间的相对顺序。Java 提供了强大的工具来实现这一功能,其中之一就是 Comparator 接口。本文档旨在详细记录 Comparator 在 Java 中的应用,特别是它在集合(如 Set, List, Map)和数组中的使用方法。

1. 定义

Comparator 是一个接口,定义了如何比较两个对象的顺序。它通常用于需要自定义排序规则的场合,尤其是在集合类(如 List 和 Set)中。Comparator 接口只有一个方法 compare(T o1, T o2),用于比较两个对象 o1 和 o2 的顺序。
下面是 Comparator 接口的基本定义:

public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}

方法详解

compare(T o1, T o2):此方法用于比较两个对象 o1 和 o2 的顺序。返回值的含义如下:

  • 如果 o1 小于 o2,则返回负整数,表明第一个对象应该排在第二个对象之前;
  • 如果 o1 等于 o2,则返回零,则表明两个对象相等;
  • 如果 o1 大于 o2,则返回正整数,则表明第一个对象应该排在第二个对象之后。

equals(Object obj):此方法是从 Object 类继承来的,并不是 Comparator 接口的一部分。它用于判断两个比较器是否等价,即它们是否会产生相同的排序结果。

实现原理

Comparator 接口的主要作用是在集合操作中提供一种外部排序机制。通常,集合框架中的排序操作会调用比较器的 compare 方法来确定元素之间的相对顺序。

常用的 Comparator 静态工厂方法

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

    • 根据提供的 keyExtractor 函数来提取键进行比较。
  2. comparingInt(Function<T, Integer> keyExtractor)

    • 根据提供的 keyExtractor 函数提取 int 类型的键进行比较。
  3. comparingLong(Function<T, Long> keyExtractor)

    • 根据提供的 keyExtractor 函数提取 long 类型的键进行比较。
  4. comparingDouble(Function<T, Double> keyExtractor)

    • 根据提供的 keyExtractor 函数提取 double 类型的键进行比较。
  5. naturalOrder()

    • 返回一个自然顺序的比较器,适用于实现了 Comparable 接口的类型。
  6. reversedOrder()

    • 返回一个逆序的比较器,适用于实现了 Comparable 接口的类型。
  7. reversed()

    • 反转现有的比较器。
  8. thenComparing(Comparator<? super T> other)

    • 在当前比较器的基础上添加另一个比较器作为次级比较器。
// 定义 Person 类
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35),
new Person("David", 30)
);
// 创建升序比较器(按年龄)
Comparator<Person> byAgeAscending = Comparator.comparingInt(Person::getAge);
// 创建降序比较器(按年龄)
Comparator<Person> byAgeDescending = byAgeAscending.reversed();
// 创建多条件比较器(先按年龄升序,再按名字升序)
Comparator<Person> byAgeThenName = Comparator.comparingInt(Person::getAge)
.thenComparing(Comparator.comparing(Person::getName));
// 创建自然顺序比较器(按名字)
Comparator<Person> byNameNaturalOrder = Comparator.comparing(Person::getName);
// 创建逆序比较器(按名字)
Comparator<Person> byNameReversedOrder = byNameNaturalOrder.reversed();
// 使用升序比较器对列表进行排序
List<Person> sortedByAgeAscending = people.stream()
.sorted(byAgeAscending)
.collect(Collectors.toList());
// 使用降序比较器对列表进行排序
List<Person> sortedByAgeDescending = people.stream()
.sorted(byAgeDescending)
.collect(Collectors.toList());
// 使用多条件比较器对列表进行排序
List<Person> sortedByAgeThenName = people.stream()
.sorted(byAgeThenName)
.collect(Collectors.toList());
// 使用自然顺序比较器对列表进行排序
List<Person> sortedByNameNaturalOrder = people.stream()
.sorted(byNameNaturalOrder)
.collect(Collectors.toList());
// 使用逆序比较器对列表进行排序
List<Person> sortedByNameReversedOrder = people.stream()
.sorted(byNameReversedOrder)
.collect(Collectors.toList());
// 打印排序结果
System.out.println("Original list: \n" + people);
System.out.println("Sorted by age ascending: \n" + sortedByAgeAscending);
System.out.println("Sorted by age descending: \n" + sortedByAgeDescending);
System.out.println("Sorted by age then name: \n" + sortedByAgeThenName);
System.out.println("Sorted by name natural order: \n" + sortedByNameNaturalOrder);
System.out.println("Sorted by name reversed order: \n" + sortedByNameReversedOrder);
}
}
Main.main(new String[] {})
Original list:
[Person{name='Alice', age=30}, Person{name='Bob', age=25}, Person{name='Charlie', age=35}, Person{name='David', age=30}]
Sorted by age ascending:
[Person{name='Bob', age=25}, Person{name='Alice', age=30}, Person{name='David', age=30}, Person{name='Charlie', age=35}]
Sorted by age descending:
[Person{name='Charlie', age=35}, Person{name='Alice', age=30}, Person{name='David', age=30}, Person{name='Bob', age=25}]
Sorted by age then name:
[Person{name='Bob', age=25}, Person{name='Alice', age=30}, Person{name='David', age=30}, Person{name='Charlie', age=35}]
Sorted by name natural order:
[Person{name='Alice', age=30}, Person{name='Bob', age=25}, Person{name='Charlie', age=35}, Person{name='David', age=30}]
Sorted by name reversed order:
[Person{name='David', age=30}, Person{name='Charlie', age=35}, Person{name='Bob', age=25}, Person{name='Alice', age=30}]
  • Comparator.comparingInt(Person::getAge):创建一个按年龄升序的比较器。
  • byAgeAscending.reversed():创建一个按年龄降序的比较器。
  • Comparator.comparingInt(Person::getAge).thenComparing(Comparator.comparing(Person::getName)):创建一个多条件比较器,先按年龄升序,如果年龄相同则按名字升序。
  • Comparator.comparing(Person::getName):创建一个按名字自然顺序的比较器。
  • byNameNaturalOrder.reversed():创建一个按名字逆序的比较器。

通过这些方法,您可以灵活地对 Person 对象列表进行各种排序操作。希望这能帮助您更好地理解和使用 Comparator

注意事项

  • 当使用 Comparator 时,必须确保 compare 方法的实现是合理的,即它应该满足比较关系的传递性和对称性。
  • 如果 Comparator 比较的对象可能为 null,需要特别注意处理 null 值的情况。
  • 在多线程环境中使用 Comparator 时,应确保其线程安全。

2. 比较对象数组

在 Java 中,可以通过实现 Comparator 接口来创建一个比较器,通过对象属性的比较进行排序。

import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Person[] people = new Person[]{
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35)
};
// 使用自定义 Person 类定义 Comparator
Arrays.sort(people, new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.getAge(), p2.getAge());
}
});
// 打印排序后的数组
System.out.println("Sorted array by age using anonymous inner class: " );
for(Person temp:people){
System.out.println(temp.toString());
}
}
}
// [IJava] 调用main函数,查看 main()函数执行结果 在Jupyter notebook中的输出信息
Main.main(new String [] {})
Sorted array by age using anonymous inner class:
Person{name='Bob', age=25}
Person{name='Alice', age=30}
Person{name='Charlie', age=35}

使用 Lambda 表达式

从 Java 8 开始,可以使用 Lambda 表达式来简化 Comparator 的实现。

import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Person[] people = new Person[]{
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35)
};
// 使用 Lambda 表达式定义 Comparator
Arrays.sort(people, (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));
// 使用 Lambda 表达式定义多条件的 Comparator
Arrays.sort(people, Comparator.comparingInt(Person::getAge) // 按照年龄排序
.thenComparingInt(p -> p.getName().length()) // 按名字的长度排序
.reversed()); // 反转年龄排序
// 打印排序后的数组
System.out.println("Sorted array by age using anonymous inner class: " );
for(Person temp:people){
System.out.println(temp.toString());
}
}
}
Main.main(new String[] {})
Sorted array by age using anonymous inner class:
Person{name='Bob', age=25}
Person{name='Alice', age=30}
Person{name='Charlie', age=35}

3. Comparator 的应用场景

应用于 List

在 List 中,可以使用 Collections.sort(List list, Comparator<? super T> c) 方法来对列表进行排序。

import java.util.ArrayList;
import java.util.Collections;
public class Main {
public static void main(String[] args) {
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Alice", 30));
people.add(new Person("Bob", 25));
people.add(new Person("Charlie", 35));
// 使用 Lambda 表达式简化代码
Collections.sort(people, (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));
// 打印排序后的列表
System.out.println("Sorted list by age using Lambda: " );
for(Person temp:people){
System.out.println(temp.toString());
}
}
}
Main.main(new String[] {})
Sorted list by age using Lambda:
Person{name='Bob', age=25}
Person{name='Alice', age=30}
Person{name='Charlie', age=35}

应用于 Set

虽然 Set 本身不支持排序,但 TreeSet 类实现了 SortedSet 接口,允许使用 Comparator 来控制元素的排序。

import java.util.TreeSet;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
TreeSet<Person> people = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.getAge(), p2.getAge());
}
});
people.add(new Person("Alice", 30));
people.add(new Person("Bob", 25));
people.add(new Person("Charlie", 35));
// 打印排序后的集合
System.out.println("Sorted set by age: " );
for(Person temp:people){
System.out.println(temp.toString());
}
}
}
Main.main(new String[] {})
Sorted set by age:
Person{name='Bob', age=25}
Person{name='Alice', age=30}
Person{name='Charlie', age=35}

应用于 Map

Map 本身不支持排序,但如果需要对 Map 的键或值进行排序,可以使用 TreeMap 并提供一个 Comparator。

import java.util.TreeMap;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
TreeMap<String, Integer> map = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
map.put("apple", 1);
map.put("banana", 2);
map.put("cherry", 3);
// 打印排序后的映射
System.out.println("Sorted map by key length: " );
// 遍历 Map 的键值对
// 因为 TreeMap 是基于红黑树实现的,它保证了键的有序性,但同时也会自动去除重复的键。因此后来插入的键值对将会覆盖前面的键值对
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
Main.main(new String[] {})
Sorted map by key length:
apple: 1
banana: 3

Comparator 接口在 Java 中提供了强大的排序功能,允许开发者自定义对象的比较逻辑。无论是集合还是数组,都可以通过实现 Comparator 接口来控制排序顺序。掌握 Comparator 的使用方法,对于开发高质量的应用程序至关重要。

本文作者:黑心老魔

本文链接:https://www.cnblogs.com/JoGyro/p/18526109

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   黑心老魔  阅读(238)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起