Sorting One List Based on Another List in Java – 用 Java 根据一个列表对另一个列表排序

最后修改: 2023年 12月 28日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Sorting a list based on the order of another list is a common task in Java, and various approaches exist to achieve this.

根据另一个列表的顺序对列表进行排序是 Java 中的一项常见任务,有多种方法可以实现这一目标。

In this tutorial, we’ll see different ways of sorting a list based on another list in Java.

在本教程中,我们将了解在 Java 中根据另一个列表对列表进行排序的不同方法。

2. Example

2.示例

Let’s consider a scenario where we’ve got a list of products as productList and another list as shoppingCart, which represents the user’s shopping cart. The shoppingCart contains various product IDs, and we need to display the products in the order they appear in the shopping cart:

让我们考虑这样一种情况:我们有一个产品列表 productList 和另一个列表 shoppingCart, 它代表用户的购物车。shoppingCart 包含各种产品 ID,我们需要按照产品在购物车中出现的顺序显示它们:

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

In the above example, the productList is the list with actual order, and shoppingCart is the list that needs to be sorted based on the productList. After sorting, the order should be:

在上述示例中,productList是具有实际顺序的列表,而 shoppingCart 是需要根据 productList 进行排序的列表。排序后,顺序应为: <em

Pizza
Burger
Fries
Coke

3. Using a for Loop to Iterate a List

3.使用 for 循环迭代 列表</em

We can use the standard for loop to sort the list based on the other list. In this approach, we create a new list that will return the elements in the sorted order. The loop iterates through the listWithOrder list and adds elements from listToSort to the sortedList in the specified order. The result is a sortedList according to the order of the elements in the listWithOrder list:

我们可以使用标准 for 循环,根据另一个列表对列表进行排序。在这种方法中,我们创建了一个新列表,该列表将按排序顺序返回元素。循环遍历 listWithOrder 列表,并按指定顺序将 listToSort 中的元素添加到 sortedList 中。结果是一个按照 listWithOrder list 中元素顺序排列的 sortedList

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

Let’s test this approach to sort the above example:

让我们用这种方法对上述例子进行排序测试:

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);
}

4. Using Comparator Interface

4.使用比较器接口

In this approach, we’re using the flexibility of Java’s Comparator interface to create a custom comparator. The comparator will be based on the indices of elements in the reference list or list with actual order. Let’s take a look at how it allows us to sort the list:

在这种方法中,我们使用 Java 的 比较器接口的灵活性来创建自定义比较器。比较器将基于引用列表或具有实际顺序的列表中元素的索引。让我们看看它是如何让我们对列表进行排序的:

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

The Comparator.comparingInt(listWithOrder::indexOf) construct allows us to sort listToSort list by order of appearance of its elements in listWithOrder.

Comparator.comparingInt(listWithOrder::indexOf)构造允许我们按照 listWithOrder 中元素的出现顺序对 listToSort 列表进行排序。</em

Let’s use this approach to sort the example discussed above:

让我们用这种方法对上面讨论的例子进行排序:

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);
}

It is a concise and readable solution that avoids the need for additional data structures and provides a clear and straightforward way. However, it’s important to note that the performance may degrade for large lists, as the indexOf() operation has a linear time complexity.

这是一种简洁易读的解决方案,它避免了对额外数据结构的需求,并提供了一种清晰明了的方法。不过,需要注意的是,由于 indexOf() 操作的时间复杂度为线性,因此对于大型列表来说,性能可能会下降。

5. Using the Stream API

5.使用流应用程序接口

We can also use the Stream API-based approach for sorting a list based on another list. First, we’ll create a mapping between elements and their indices in listWithOrder through Collectors.toMap() collector. After that, the resultant map will be used to sort listToSort with Comparator.comparingInt() method:

我们还可以使用基于 Stream API 的方法,根据另一个列表对列表进行排序。首先,我们将通过 Collectors.toMap() CollectorlistWithOrder 中创建元素与其索引之间的映射。之后,我们将使用 Comparator.comparingInt() 方法对 listToSort 进行排序:

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));
}

Let’s test this approach to sort the above example:

让我们用这种方法对上述例子进行排序测试:

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

The Stream API approach provides a clean and modern solution. However, it’s crucial to be mindful of the potential overhead for large lists, as creating the map involves iterating over the entire list.

流 API 方法提供了一种简洁而现代的解决方案。然而,必须注意大型列表的潜在开销,因为创建映射涉及到对整个列表的迭代。

6. Using a Map

6.使用地图</em

In this approach, we leverage the power of Java’s Map to create a direct mapping between elements in the reference list listWithOrder and their corresponding indices. The key-value pairs in the map consist of elements from listWithOrder as keys and their indices as values:

在这种方法中,我们利用 Java 的 Map 功能在引用列表 listWithOrder 中的元素与其对应的索引之间创建直接映射。映射中的键值对包括作为键的 listWithOrder 中的元素和作为值的其索引:</em

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));
}

Let’s test this approach to sort the above example:

让我们用这种方法对上述例子进行排序测试:

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);
}

The Use of Map provides us with an advantage over the indexOf() method, especially in scenarios involving large lists, repeated lookups, or performance-sensitive applications.

Map 的使用为我们提供了优于 indexOf() 方法的优势,尤其是在涉及大型列表、重复查找或对性能敏感的应用中。

7. Using Guava’s Ordering.explicit()

7.使用 Guava 的 Ordering.explicit()

Guava is a widely used Java library that provides a convenient method for sorting a list based on the order of elements of another list. Let’s start by adding this dependency in our pom.xml file:

Guava 是一个广泛使用的 Java 库,它提供了一种方便的方法,可根据另一个列表中元素的顺序对列表进行排序。首先,让我们在 pom.xml 文件中添加 此依赖关系

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.0.0-jre</version>
</dependency>

Guava’s explicit() method allows us to create a comparator based on a specific order. The Ordering class is immutable, so the result will be a new sorted list, and the original list, i.e., listToSort, will remain unchanged.

Guava 的 explicit() 方法允许我们根据特定顺序创建比较器。Ordering 类是不可变的,因此结果将是一个新的排序列表,而原始列表,即 listToSort, 将保持不变。

List<String> sortUsingGuava(List<String> listToSort, List<String> listWithOrder) {
    Ordering<String> explicitOrdering = Ordering.explicit(listWithOrder);
    List<String> sortedList = explicitOrdering.sortedCopy(listToSort);
    return sortedList;
}

In the above example, the sortedCopy() method is responsible for creating a sorted list. Let’s test this approach:

在上例中,sortedCopy() 方法负责创建排序列表。让我们测试一下这种方法:

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. Using Vavr

8.使用 Vavr

Vavr is a functional library for Java 8+ that provides immutable data types and functional control structures. In order to use Vavr, we first need to add this dependency:

Vavr 是一个适用于 Java 8+ 的 功能库,可提供不可变的数据类型和功能控制结构。要使用 Vavr,我们首先需要添加此依赖关系

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.10.4</version>
</dependency>

Vavr provides a sortBy()  method that can be used to sort a list, i.e., listToSort, based on the order specified in another list, i.e., listToOrder. The result will be stored in a new list sortedList, and the original listToSort list will remain unchanged. Let’s see an example using Vavr:

Vavr 提供了一个 sortBy() 方法,可用于根据另一个列表(即 listToOrder 中指定的顺序对列表(即 listToSort )进行排序。结果将存储在一个新的列表 sortedList 中,而原来的 listToSort 列表将保持不变。让我们看看使用 Vavr 的示例:

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();
}

Let’s test this approach:

让我们来测试一下这种方法:

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. Conclusion

9.结论

In this tutorial, we explored various approaches for sorting a list based on the order of elements in another list. The choice of the appropriate approach relies on the specific use case based on the simplicity and performance of the solution.

在本教程中,我们探讨了根据另一个列表中元素的顺序对列表进行排序的各种方法。如何选择合适的方法取决于具体的使用情况,取决于解决方案的简单性和性能。

As always, the source code is available over on GitHub.

与往常一样,源代码可在 GitHub 上获取