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仓库获取完整实现。