[Java SE] Java 排序接口: `Comparable` / `Comparator`

概述:Java 排序接口: Comparable / Comparator

  • 在 Java 中,排序功能主要通过 Comparable 接口和 Comparator 接口来实现。

这两个接口在功能和使用场景上有一些区别,以下是它们的详细比较:

java.lang.Comparable 接口

  • 定义

    • Comparable 是一个内置的 Java 接口,定义在 java.lang 包中。
    • 它允许一个类实现一个自然排序(natural ordering),即根据对象的某个属性进行排序。
  • 方法

    • int compareTo(T o):比较当前对象与参数对象的大小。返回值:
      • 负数:当前对象小于参数对象。
      • 零:当前对象等于参数对象。
      • 正数:当前对象大于参数对象。
  • 使用场景

    • 当类的实例需要按照某种固定的、自然的顺序排序时,可以实现 Comparable 接口。
    • 例如,String 类实现了 Comparable 接口,按照字典顺序排序;Integer 类实现了 Comparable 接口,按照数值大小排序。
  • 优点

    • 提供了自然排序的方式,使得类的实例在默认情况下可以被排序。
    • 可以直接使用 Arrays.sort()Collections.sort() 对实现了 Comparable 接口的类的实例进行排序。
  • 缺点

    • 一个类只能实现一种自然排序方式。如果需要多种排序方式,则需要额外的机制。

java.util.Comparator 接口

  • 定义
    • Comparator 是一个函数式接口,定义在 java.util 包中。
    • 它允许定义一个外部的比较器,用于对类的实例进行排序。
  • 方法
    • int compare(T o1, T o2):比较两个对象的大小。返回值:
      • 负数:o1 小于 o2
      • 零:o1 等于 o2
      • 正数:o1 大于 o2
    • boolean equals(Object obj):用于比较两个比较器是否相等。
  • 使用场景
    • 当需要对类的实例进行多种排序方式时,可以定义多个 Comparator
    • 例如,对于一个 Person 类,可以定义一个按年龄排序的 Comparator 和一个按姓名排序的 Comparator
  • 优点
    • 提供了灵活的排序方式,可以定义多个比较器来满足不同的排序需求。
    • 可以在不修改类代码的情况下,为类的实例提供多种排序方式。
  • 缺点
    • 需要额外定义比较器,增加了代码的复杂性。
    • 使用时需要显式指定比较器,不如 Comparable 接口的自然排序直观。

比较与选择

  • 单一排序需求
    • 如果类的实例只需要一种固定的排序方式,建议实现 Comparable 接口。
    • 例如,Integer 类只需要按照数值大小排序,因此实现 Comparable 接口是合适的。
  • 多种排序需求
    • 如果类的实例需要多种排序方式,建议使用 Comparator 接口。
    • 例如,Person 类可能需要按年龄排序,也可能需要按姓名排序,此时可以定义多个 Comparator
  • 灵活性与扩展性
    • Comparator 接口提供了更高的灵活性和扩展性,适合复杂的排序需求。
    • Comparable 接口则更简单、更直观,适合单一的排序需求。

代码示例

实现 Comparable 接口

import java.util.*;

class Person implements Comparable<Person> {
    private String name;
    private int age;

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

    @Override
    public int compareTo(Person other) {
        return this.age - other.age; // 按年龄排序
    }

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

public class Main {
    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        Collections.sort(people); // 使用 Comparable 接口排序
        System.out.println(people);
    }
}

使用 Comparator 接口

import java.util.*;

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 + "}";
    }
}

public class Main {
    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        // 按年龄排序
        Collections.sort(people, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        });

        System.out.println(people);

        // 按姓名排序
        people.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
        System.out.println(people);
    }
}

使用 Comparator 接口对List元素排序(项目案例)

DataElementSubElementNoComparator

package com.xxx.sdk.java.someip.parse.sort;

import com.xxx.sdk.pojo.someip.entity.DataElementInfo;

import java.util.Comparator;

/**
 * DataElementInfo SubElementNo 字段排序器
 * @note
 *   1. Comparator 是一个策略接口,用于定义自定义排序规则;
 *   2. 你可以在不修改类本身的情况下,通过实现 Comparator 来定义多个排序规则
 * @reference-doc
 *  [1] Java 排序神器:Comparable 和 Comparator 该怎么选? - Zhihu - https://zhuanlan.zhihu.com/p/17041375356
 */
public class DataElementSubElementNoComparator implements Comparator<DataElementInfo> {
    /**
     * 根据 SubElementNo 升序排序
     * @param o1 the first object to be compared.
     * @param o2 the second object to be compared.
     * @return
     */
    @Override
    public int compare(DataElementInfo o1, DataElementInfo o2) {
        if(o1.getSubElementNo() > o2.getSubElementNo()){
            return 1;
        } else if(o1.getSubElementNo() < o2.getSubElementNo()){
            return -1;
        }
        return 0;
    }
}

排序器的使用

...

    public static DataElementDefinitions convertToDataElementDefinitions(Map<String, List<DataElementInfo>> dataElementInfoListMap, Boolean structSorted){
        Boolean isRecursiveNode = true;//是否完全递归, 以设置父节点和子节点
        //DataElementDefinitions dataElementDefinitions = parseMatrixAsFlatMapDataElementDefinitions(someIpMatrixFilePath);
        if(!structSorted){//结构体元素是否已升序排序(否则,影响结构体的解析顺序)
            dataElementInfoListMap.entrySet().stream().forEach(dataElementInfoLisEntry -> {
                List<DataElementInfo> dataElementInfoList = dataElementInfoLisEntry.getValue();
                if(dataElementInfoList != null && dataElementInfoList.size() > 1){
                    dataElementInfoList.sort(new DataElementSubElementNoComparator());//←←←←←←←←←←
                }
            });
        }
        DataElementDefinitions dataElementDefinitions = convertToFlatMapDataElementDefinitions(dataElementInfoListMap);
        if(isRecursiveNode){
            dataElementDefinitions.entrySet().stream().forEach( dataElementDefinitionEntry -> {
                String parameterName = dataElementDefinitionEntry.getKey();
                DataElementDefinition dataElementDefinition = dataElementDefinitionEntry.getValue();
                recursiveNodeDataElementDefinitions( dataElementDefinition, dataElementDefinitions );
            });
        }
        return dataElementDefinitions;
    }

...

Collections.reverseOrder() 进行逆序

public class Test5 {
    public static void main(String[] args) {
        Integer[] arr = {9, 8, 7, 2, 3, 4, 1, 0, 6, 5};
        Arrays.sort(arr, Collections.reverseOrder());
        for(int x:arr){
            System.out.print(x); //print: 9876543210
        }
    }
}

基于 List.sort(Comparator 接口) 实现对 List 对象自身的元素进行排序

  • java.util.List#sort
void sort(Comparator<? super E> var1)
  • demo
public class ListSortTest {
    private final static Logger log = LoggerFactory.getLogger(ListSortTest.class);

    @Test
    public void sortTest(){
        List<ParsedCycleSequenceVo> cycleSequences = new ArrayList<>();
        ParsedCycleSequenceVo cycleCanSequence1 = new ParsedCycleSequenceVo();cycleSequence1.setCollectTs( 1L );
        ParsedCycleSequenceVo cycleCanSequence2 = new ParsedCycleSequenceVo();cycleSequence2.setCollectTs( 3L );
        ParsedCycleSequenceVo cycleCanSequence3 = new ParsedCycleSequenceVo();cycleSequence3.setCollectTs( 2L );

        cycleSequences.add( cycleCanSequence1 );
        cycleSequences.add( cycleCanSequence2 );
        cycleSequences.add( cycleCanSequence3 );

        log.info("cycleSequences:{}", JSON.toJSONString( cycleSequences ));//[{"collect_ts":1},{"collect_ts":3},{"collect_ts":2}]

        cycleSequences.sort( new Comparator<ParsedCycleSequenceVo>() {
            @Override
            public int compare(ParsedCycleSequenceVo o1, ParsedCycleSequenceVo o2) {
                return o1.getCollectTs().compareTo( o2.getCollectTs() );//升序
            }
        });

        log.info("cycleSequences:{}", JSON.toJSONString( cycleSequences ));//[{"collect_ts":1},{"collect_ts":2},{"collect_ts":3}]
    }
}

X 参考文献

posted @ 2025-07-08 19:38  千千寰宇  阅读(32)  评论(0)    收藏  举报