1. 概述

在 Java 开发中,我们经常需要处理成对的数据,而 Apache Commons Lang3 库提供了便捷的 Pair 类来满足这种需求。当面对一个 Pair<String, Integer> 列表时,我们常常需要根据其中的整数值进行排序。

本文将探讨多种对 Apache Commons Lang3 的 Pair<String, Integer> 列表按整数值排序的方法。

2. 问题引入

Apache Commons Lang3 中的 Pair 类提供了一种简单的结构来存储两个值。作为 Java 中常用的 Pair 类型之一,我们将在本教程中以此为例。

通过示例理解问题。首先创建一个生成 Pair<String, Integer> 列表的方法:

private List<Pair<String, Integer>> getUnsortedInput() {
    return Arrays.asList(
      Pair.of("False", 5),
      Pair.of("Yes", 3),
      Pair.of("True", 4),
      Pair.of("No", 2),
      Pair.of("X", 1)
    );
}

如代码所示,getUnsortedInput() 方法生成一个未排序的 List<Pair<String, Integer>>。**每个 Pair 元素包含两个值:一个 String 和对应的字母数量 Integer

我们的目标是根据每个 Pair 元素的整数值对列表进行排序。预期结果如下:

static final List<Pair<String, Integer>> EXPECTED = List.of(
  Pair.of("X", 1),
  Pair.of("No", 2),
  Pair.of("Yes", 3),
  Pair.of("True", 4),
  Pair.of("False", 5)
);

接下来我们将探索解决这个排序问题的不同方法。

3. 使用匿名比较器类

要对数据集合排序,我们需要比较元素。Apache Commons Lang3 的 Pair 类未实现 Comparable 接口,因此无法直接比较两个 Pair 对象

但我们可以创建一个实现 Comparator 接口的匿名类,通过比较两个 Pair 的整数值,然后将该 Comparator 传递给 List.sort()

List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort(new Comparator<Pair<String, Integer>>() {
    @Override
    public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
        return o1.getRight()
          .compareTo(o2.getRight());
    }
});
 
assertEquals(EXPECTED, myList);

如代码所示,匿名 Comparator 类实现了 compare() 方法。由于 Integer 实现了 Comparable,我们使用 Integer.compareTo() 比较两个 Pair 元素的整数值。

值得一提的是,Apache Commons Lang3 的 Pair 类提供了 getRight()getValue() 方法来返回 Pair 对象的第二个值。这两个方法完全等价。实际上,getValue() 内部调用了 *getRight()*:

public abstract R getRight();
 
public R getValue() {
    return this.getRight();
}

运行测试后通过,证明此方法有效。

4. 使用 Lambda 表达式

匿名比较器类解决了排序问题,但代码可读性较差。*自 Java 8 起,List.sort()* 方法支持函数式特性:使用 Lambda 表达式作为 Comparator**。

下面将匿名比较器类重构为 Lambda 表达式:

List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort((p1, p2) -> p1.getRight()
  .compareTo(p2.getRight()));
 
assertEquals(EXPECTED, myList);

Lambda 表达式提供了实现 Comparator 接口的简洁方式,使代码更易读和维护。

5. 使用 Comparator.comparing()

自 Java 8 起,**Comparator 接口新增了 comparing() 方法。该方法接受一个 Function 类型的 keyExtractor,返回一个按该键排序的 Comparator**。

使用 Comparator.comparing() 解决排序问题:

List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort(Comparator.comparing(Pair::getRight));
 
assertEquals(EXPECTED, myList);

我们传递了 方法引用 Pair::getRight 作为 comparing() 的函数参数,创建了一个按整数值排序 List<Pair<String, Integer>>Pair 元素的 Comparator。

6. 生成新列表存储排序结果

前三种解决方案都执行原地排序,会修改原始输入列表的元素顺序。

但有时我们不能修改输入列表——例如当输入是不可变列表时:

List<Pair<String, Integer>> immutableUnsortedList = List.copyOf(getUnsortedInput());
assertThrows(UnsupportedOperationException.class, 
  () -> immutableUnsortedList.sort(Comparator.comparing(Pair::getRight)));

此例使用 List.copyOf() 创建不可变列表,尝试排序时会抛出异常。

因此需要将排序结果存储在新的 List 对象中

一种简单思路是先复制原始列表,再对副本进行原地排序。

另一种方法是使用 Streamsorted() 方法排序,然后将排序后的元素收集到新列表:

List<Pair<String, Integer>> immutableUnsortedList = List.copyOf(getUnsortedInput());
 
List<Pair<String, Integer>> sorted = immutableUnsortedList.stream()
  .sorted(Comparator.comparing(Pair::getRight))
  .toList();
 
assertEquals(EXPECTED, sorted);

stream().sorted().toList() 链式调用简洁易读,流畅地解决了问题*。

7. 总结

本文探讨了多种对 List<Pair<String, Integer>> 按整数值排序的方法,并讨论了如何生成新列表存储排序结果而不修改原始列表。

所有示例的完整源代码可在 GitHub 获取。


原始标题:How to Sort a List of Pair | Baeldung