如何根据特定属性的另一个类型列表对对象列表进行排序

发布于 2025-02-04 14:33:45 字数 1490 浏览 2 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

少女七分熟 2025-02-11 14:33:45

幼稚的方法

是一种天真的方法是使用使用concamingInt()创建,以通过匹配名称检索元素的索引。然后在第一个列表上应用sort()

如果equals()hashcode()不仅基于name属性,我们可以创建一个包装程序类并实现其<代码>等于/hashcode 合同的方式仅在比较期间考虑name

然后基于list1list2生成两个列表employeewrapper对象(下面的代码不完整,仅提供以说明这个想法)。

public class EmployeeWrapper {
    private String name;
    private Employee employee;
    
    // getters, constructor, equals/hashCode
}
Comparator<EmployeeWrapper> comparator = Comparator.comparingInt(wrappedList2::indexOf);

wrappedList1.sort(comparator);

List<Employee> sortedList = wrappedList.stream()
    .map(EmployeeWrapper::getEmployee)
    .collect(Collectors.toList());

但是,多次迭代第二列表第二列表将非常低效。确切地说,时间复杂性将为 o(m * n log n)(其中n - 第一个列表中的元素数,m - 第二列表的大小)。

由于第二个列表中的元素位置不会更改,因此在地图中存储索引

,我们可以单个通过列表中的索引并将其存储在 map 中。然后基于此 map 生成A 比较器

时间复杂性将为 o(m + n log n)这是指第二列表的影响要高得多)。

public static Comparator<Employee> compareByName(List<Employee> base) {
    
    Map<String, Integer> indexByName = getIndexByName(base);
    
    return Comparator.comparingInt(employee ->
        indexByName.getOrDefault(employee.getName(), Integer.MAX_VALUE)); // employee that have no matching pair will be grouped at the end of the resulting list
}

public static Map<String, Integer> getIndexByName(List<Employee> list) {
    
    Map<String, Integer> indexByName = new HashMap<>();
    
    for (int i = 0; i < list.size(); i++) {
        indexByName.putIfAbsent(list.get(i).getName(), i);
    }
    return indexByName;
}

main()

public static void main(String[] args) {

    List<Employee> list1 = // initialing the list

    List<Employee> list2 = // initialing the list

    Comparator<Employee> comparator = compareByName(list2);
    list1.sort(comparator);

    System.out.println("##### List-1 ####");
    list1.forEach(System.out::println);
    System.out.println("##### List-2 ####");
    list2.forEach(System.out::println);
}

在某些条件下,可以进一步优化此代码。

例如,如果没有重复的名称,则可以使其在A 线性时间中运行。

Naive approach

A naive way would be to create Comparator using comparingInt() by the logic for retrieving the indexed of the element with the matching name. And then apply sort() on the first list.

In case if equals() and hashCode() are not based on the name property only, we can create a wrapper class and implement its equals/hashCode contract in such a way that only name will be taken into account during comparison.

And then based on the list1 and list2 generate two lists EmployeeWrapper objects (the code below is incomplete and provided only to illustrate the idea).

public class EmployeeWrapper {
    private String name;
    private Employee employee;
    
    // getters, constructor, equals/hashCode
}
Comparator<EmployeeWrapper> comparator = Comparator.comparingInt(wrappedList2::indexOf);

wrappedList1.sort(comparator);

List<Employee> sortedList = wrappedList.stream()
    .map(EmployeeWrapper::getEmployee)
    .collect(Collectors.toList());

But it would be terribly inefficient to iterate over the second list multiple times. To be precise, time complexity would be O(m * n log n) (where n - number of elements in the first list, m - the size of the second list).

Storing indices in the Map

Since the positions of the elements in the second list don't change, we can index them in a single pass through the list and store it in the map. And then generate a comparator based on this map.

The time complexity would be O(m + n log n) (it means the impact of the second list far more moderate).

public static Comparator<Employee> compareByName(List<Employee> base) {
    
    Map<String, Integer> indexByName = getIndexByName(base);
    
    return Comparator.comparingInt(employee ->
        indexByName.getOrDefault(employee.getName(), Integer.MAX_VALUE)); // employee that have no matching pair will be grouped at the end of the resulting list
}

public static Map<String, Integer> getIndexByName(List<Employee> list) {
    
    Map<String, Integer> indexByName = new HashMap<>();
    
    for (int i = 0; i < list.size(); i++) {
        indexByName.putIfAbsent(list.get(i).getName(), i);
    }
    return indexByName;
}

main()

public static void main(String[] args) {

    List<Employee> list1 = // initialing the list

    List<Employee> list2 = // initialing the list

    Comparator<Employee> comparator = compareByName(list2);
    list1.sort(comparator);

    System.out.println("##### List-1 ####");
    list1.forEach(System.out::println);
    System.out.println("##### List-2 ####");
    list2.forEach(System.out::println);
}

In certain conditions, this code can be optimized further.

For instance, if there would be no duplicated names, it's possible to make it run in a linear time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文