1. 概述

在Java开发中,根据一个列表的顺序对另一个列表进行排序是常见需求。本文将探讨多种实现方案,包括原生Java方法和第三方库支持。

2. 示例场景

假设我们有两个列表:productList包含所有商品,shoppingCart表示用户购物车中的商品ID。现在需要按照购物车中的顺序显示商品:

List<String> productList = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
List<String> shoppingCart = Arrays.asList("Pizza", "Burger", "Fries", "Coke");

排序目标:shoppingCart按照productList的顺序重新排列,最终结果应为:

Pizza
Burger
Fries
Coke

3. 使用for循环实现

最直观的方案是通过循环遍历参考列表,构建新列表:

List<String> sortUsingForLoop(List<String> listToSort, List<String> listWithOrder) {
    List<String> sortedList = new ArrayList<>();
    for (String element: listWithOrder) {
        if (listToSort.contains(element)) {
            sortedList.add(element);
        }
    }
    return sortedList;
}

测试用例:

public void givenTwoList_whenUsingForLoop_thenSort() {
    List<String> listWithOrder = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    List<String> listToSort = Arrays.asList("Pizza", "Burger", "Fries", "Coke");
    sortUsingForLoop(listToSort, listWithOrder);
    List<String> expectedSortedList = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    assertEquals(expectedSortedList, listWithOrder);
}

优点:实现简单直观
缺点:时间复杂度为O(n²),大数据量时性能较差

4. 使用Comparator接口

利用Java的Comparator接口创建自定义比较器:

void sortUsingComparator(List<String> listToSort, List<String> listWithOrder) {
    listToSort.sort(Comparator.comparingInt(listWithOrder::indexOf));
}

测试用例:

public void givenTwoList_whenUsingComparator_thenSort() {
    List<String> listWithOrder = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    List<String> listToSort = Arrays.asList("Pizza", "Burger", "Fries", "Coke");
    sortUsingComparator(listToSort, listWithOrder);
    List<String> expectedSortedList = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    assertEquals(expectedSortedList, listToSort);
}

⚠️ 注意:虽然代码简洁,但indexOf()操作的时间复杂度为O(n),大数据量时性能会下降

5. 使用Stream API

通过Stream API创建索引映射再排序:

void sortUsingStreamAPI(List<String> listToSort, List<String> listWithOrder) {
    Map<String,Integer> indicesMap = listWithOrder.stream()
        .collect(Collectors.toMap(e -> e, listWithOrder::indexOf));
    listToSort.sort(Comparator.comparingInt(indicesMap::get));
}

测试用例:

public void givenTwoList_whenUsingStreamAPI_thenSort() {    
    List<String> listWithOrder = Arrays.asList("Burger", "Coke", "Fries", "Pizza");    
    List<String> listToSort = Arrays.asList("Pizza", "Burger", "Fries", "Coke");
    sortUsingStreamAPI(listToSort, listWithOrder);
    List<String> expectedSortedList = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    assertEquals(expectedSortedList, listToSort);
}

优点:代码现代化,函数式风格
缺点:创建索引映射需要额外内存

6. 使用Map优化

直接创建元素到索引的映射,避免重复计算:

void sortUsingMap(List<String> listToSort, List<String> listWithOrder) {
    Map<String, Integer> orderedIndicesMap = new HashMap<>();
    for (int i = 0; i < listWithOrder.size(); i++) {
        orderedIndicesMap.put(listWithOrder.get(i), i);
    }
    listToSort.sort(Comparator.comparingInt(orderedIndicesMap::get));
}

测试用例:

public void givenTwoList_whenUsingMap_thenSort() {
    List<String> listWithOrder = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    List<String> listToSort = Arrays.asList("Pizza", "Burger", "Fries", "Coke");
    sortUsingMap(listToSort, listWithOrder);
    List<String> expectedSortedList = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    assertEquals(expectedSortedList, listToSort);
}

优点

  • 时间复杂度优化到O(n)
  • 适合大数据量场景
  • 比Stream API更节省内存

7. 使用Guava的Ordering.explicit()

引入Guava库简化实现:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.0.0-jre</version>
</dependency>
List<String> sortUsingGuava(List<String> listToSort, List<String> listWithOrder) {
    Ordering<String> explicitOrdering = Ordering.explicit(listWithOrder);
    List<String> sortedList = explicitOrdering.sortedCopy(listToSort);
    return sortedList;
}

测试用例:

public void givenTwoList_whenUsingGuavaExplicit_thenSort() {
    List<String> listWithOrder = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    List<String> listToSort = Arrays.asList("Pizza", "Burger", "Fries", "Coke");
    sortUsingGuava(listToSort, listWithOrder);
    List<String> expectedSortedList = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    assertEquals(expectedSortedList, listWithOrder);
}

特点

  • 返回新列表,原列表保持不变
  • 代码简洁,语义清晰
  • 适合需要不可变操作的场景

8. 使用Vavr库

Vavr是Java 8+的函数式库:

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.10.4</version>
</dependency>
List<String> sortUsingVavr(List<String> listToSort, List<String> listWithOrder) {
    io.vavr.collection.List<String> listWithOrderedElements = 
        io.vavr.collection.List.ofAll(listWithOrder);
    io.vavr.collection.List<String> listToSortElements = 
        io.vavr.collection.List.ofAll(listToSort);
    io.vavr.collection.List<String> sortedList = 
        listToSortElements.sortBy(listWithOrderedElements::indexOf);
    return sortedList.asJava();
}

测试用例:

public void givenTwoList_whenUsingVavr_thenSort() {
    List<String> listWithOrder = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    List<String> listToSort = Arrays.asList("Pizza", "Burger", "Fries", "Coke");
    sortUsingVavr(listToSort, listWithOrder);
    List<String> expectedSortedList = Arrays.asList("Burger", "Coke", "Fries", "Pizza");
    assertEquals(expectedSortedList, listWithOrder);
}

特点

  • 函数式编程风格
  • 不可变数据结构
  • 适合函数式编程爱好者

9. 方案对比总结

实现方式 时间复杂度 空间复杂度 适用场景
for循环 O(n²) O(n) 小数据量,简单场景
Comparator O(n²) O(1) 代码简洁优先
Stream API O(n) O(n) 函数式风格
Map优化 O(n) O(n) 大数据量推荐
Guava O(n) O(n) 需要不可变结果
Vavr O(n) O(n) 函数式编程项目

选择建议

  • 小数据量:优先使用Comparator方案,代码最简洁
  • 大数据量:选择Map优化方案,性能最佳
  • 函数式项目:考虑Vavr或Stream API
  • 需要不可变结果:使用Guava

所有示例代码可在GitHub仓库获取完整实现。


原始标题:Sorting One List Based on Another List in Java | Baeldung