【问题标题】:Sort a list based on member field’s value’s position in another list根据成员字段的值在另一个列表中的位置对列表进行排序
【发布时间】:2022-01-13 21:32:29
【问题描述】:

我有一个 Person 字段,包括 score(int)、name(String) 和 country(String)

1, Jane , Spain
2, James, Italy
1, John , Italy
2, Peter , England
3, Thomas , England
3, Martin , England

现在我提供另一个代表国家名称的字符串列表:[Italy, Spain, Norway, England, Germany]

我的要求是首先根据分数对人员列表进行排序,如果他们具有相同的分数然后按照国家名称列表中提供的国家名称进行排序。如果国家/地区名称也匹配,则按人名排序。

所以最后应该按这个顺序返回 person 对象:

1, John, Italy
1, Jane, Spain
2, James, Italy
2, Peter, England
3, Martin, England
3, Thomas, England

这是我正在尝试的代码:

public static List<Person> sortRecords(List<Person> people, List<String> countries) {
    people.sort((p1, p2) -> {
        int compare = p1.getScore() - p2.getScore();
        if(compare == 0) {
            compare = // how to compare by country names based on provided List<String> countries
            if(compare == 0) {
                compare = p1.getName().compareTo(p2.getName());
            }
        }
        return compare;
    });
}

我一直在按国家/地区名称进行比较。

【问题讨论】:

  • 反对票有什么理由吗?
  • 那么,每个Person 都有一个国家,并且您有一个单独的国家列表(我认为这是“首选”顺序,而不是按字母顺序)?从列表中获取每个国家的索引并进行比较
  • 不是反对者,但minimal reproducible example 会帮助我们理解问题
  • countries.indexOf(p1.getCountry) - countries.indexOf(p2.getCountry)
  • 可能是因为标题和正文并不完全清楚。乍一看,您的问题与懒得搜索 Stack Overflow 的作者发布的非常常见的“如何对我的列表进行排序”问题重复。我编辑了标题以使其更加清晰和具体。

标签: java algorithm


【解决方案1】:

使用List.indexOf

public static List<Person> sortRecords(List<Person> people, List<String> countries) {
    people.sort((p1, p2) -> {
        int compare = p1.getScore() - p2.getScore();
        if(compare == 0) {
            compare = countries.indexOf(p1.getCountry()) - countries.indexOf(p2.getCountry());
            if(compare == 0) {
                compare = p1.getName().compareTo(p2.getName());
            }
        }
        return compare;
    });
    return people;
}

请注意,由于排序是就地完成的,您不需要返回列表,然后使用java.util.Comparator 可以做一些好事

public static void sortRecords(List<Person> people, List<String> countries) {
    people.sort(
         Comparator.comparingInt(Person::getScore)
                   .thenComparingInt(person -> countries.indexOf(person.getCountry()))
                   .thenComparing(Person::getName)
    );
}

【讨论】:

  • 会很贵。最好使用Map 创建反向索引查找。
  • @BoristheSpider 同意,但是对于当前的问题,这就足够了
  • 请注意,这假定该人所在的国家/地区存在于国家/地区列表中,否则他们可能最终位于列表顶部;)
  • 吹毛求疵 :) indexOf 返回 int 所以我们可以使用 thenComparingInt 直接处理它,而不是 getComparing 期望 Comparable 会强制 int 被装箱到Integer。此外,虽然p1.getScore() - p2.getScore(); 对于正值可能没问题,但使用Integer.compare(p1.getScore(), p2.getScore()); 通常是更安全的方法,它保证正确处理比较而没有溢出的风险。同样适用于indexOf
【解决方案2】:

我会使用稳定的排序算法按名称、国家/地区和分数对列表进行排序。见https://www.geeksforgeeks.org/stability-in-sorting-algorithms/

import java.util.*;
class Record {
    int score; String name; String country;
    Record(int s, String n, String c){ score=s; name=n; country=c; }
    public String toString(){ 
      return String.format("[%d, %s, %s]", score, name, country);
    }
}
public class Main{
    public static void main(String[] args) {
        Record[] records = {
              new Record(1, "Jane" ,   "Spain"),
              new Record(2, "James",   "Italy"),
              new Record(1, "John" ,   "Italy"),
              new Record(2, "Peter",   "England"),
              new Record(3, "Thomas",  "England"),
              new Record(3, "Martin", "England")
        };
        Comparator<Record> byScore   = (r1,r2)->r1.score-r2.score;
        Comparator<Record> byCountry = (r1,r2)->r1.country.compareTo(r2.country);
        Comparator<Record> byName    = (r1,r2)->r1.name.compareTo(r2.name);

        System.out.println("===before====");
        Arrays.stream(records).forEach(System.out::println);
        
        Arrays.sort(records, byName); 
        Arrays.sort(records, byCountry);     
        Arrays.sort(records, byScore);
        
        System.out.println("===after====");
        Arrays.stream(records).forEach(System.out::println);
    }
}

输出

===before====
[1, Jane, Spain]
[2, James, Italy]
[1, John, Italy]
[2, Peter, England]
[3, Thomas, England]
[3, Martin, England]
===after====
[1, John, Italy]
[1, Jane, Spain]
[2, Peter, England]
[2, James, Italy]
[3, Martin, England]
[3, Thomas, England]

【讨论】:

  • 由于 peter 和 james 得分相同,我的下一步是根据输入列表 [Italy, Spain, Norway, England, Germany] 中提供的国家/地区名称进行排序,因此在本例中为 Italy comes first then England。
  • 我误解了这个要求。 :)
猜你喜欢
  • 2011-09-30
  • 1970-01-01
  • 1970-01-01
  • 2018-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多