我正在寻找在Java中使用多个字段进行加权排序的有效实现。此问题与How to provide most relevant results with Multiple Factor Weighted Sorting和Need help maximizing 3 factors in multiple, similar objects and ordering appropriately类似。但是,我要求就有效实施提出一些指导。
在下面的示例中,Person
类包含age
和income
字段,我希望将persons
与较低age
和income
进行排序基于给定首选项和降序排列的更高age
组合。我为income
和package main.java.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class SortingTest {
static double income_preference = 0.5;
static double age_preference = 1 - income_preference;
public static void main(String args[]) {
ArrayList<Person> persons = new ArrayList<Person>();
persons.add(new Person("A", 60, 55.0));
persons.add(new Person("B", 45, 50.0));
persons.add(new Person("C", 20, 50.0));
persons.add(new Person("D", 55, 60.0));
persons.add(new Person("E", 30, 85.0));
// Sort the array list by income (descending order)
Collections.sort(persons, new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) {
return (((double)p1.income > (double)p2.income) ? -1 :
((double)p1.income < (double)p2.income) ? 1 : 0);
}
});
// Rank based on income
int income_rank = persons.size();
for(int i = 0; i < persons.size(); i++) {
if(i != 0)
if(persons.get(i).income != persons.get(i-1).income)
--income_rank;
persons.get(i).income_rank = income_rank * income_preference;
}
System.out.println("List of persons sorted by their income in descending order: ");
for(Person p : persons)
System.out.println(p.toString());
// Sort the array list by age (ascending order)
Collections.sort(persons, new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) {
return (((double)p2.age > (double)p1.age) ? -1 :
((double)p2.age < (double)p1.age) ? 1 : 0);
}
});
// Rank based on age
int age_rank = persons.size();
for(int i = 0; i < persons.size(); i++) {
if(i != 0)
if(persons.get(i).age != persons.get(i-1).age)
--age_rank;
persons.get(i).age_rank = age_rank * age_preference;
}
System.out.println();
System.out.println("List of persons sorted by their age in ascending order: ");
for(Person p : persons)
System.out.println(p.toString());
// Assign combined rank
for(Person p : persons)
p.combined_rank = (p.age_rank + p.income_rank);
// Sort the array list by the value of combined rank (descending order)
Collections.sort(persons, new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) {
return (((double)p1.combined_rank > (double)p2.combined_rank) ? -1 :
((double)p1.combined_rank < (double)p2.combined_rank) ? 1 : 0);
}
});
System.out.println();
System.out.println("List of persons sorted by their combined ranking preference in descending order: ");
for(Person p : persons)
System.out.println(p.toString());
}
}
class Person {
String name;
int age; // lower is better
double income; // higher is better
double age_rank;
double income_rank;
double combined_rank;
public Person(String name, int age, double income) {
this.name = name;
this.age = age;
this.income = income;
this.age_rank = 0.0;
this.income_rank = 0.0;
this.combined_rank = 0.0;
}
@Override
public String toString() {
return ("Person-"+this.name+", age("+this.age+"|"+this.age_rank+"th), income("+this.income+"|"+this.income_rank+"th), Combined Rank("+this.combined_rank+")");
}
}
提供了相同的偏好设置。首选项的总和应为1。
正如您在这个天真的实现中所看到的那样,有太多的循环需要迭代,并且最终它的成本太高而无法运行大量的输入。我还探讨了 Guava的ComparisonChain 和 Apache Commons CompareToBuilder ,但似乎它们没有实现我的目标。
^
控制台输出
按收入按降序排序的人员名单:
人-E,年龄(30 | 0.0th),收入(85.0 | 2.5th),综合排名(0.0)
人D,年龄(55 | 0.0th),收入(60.0 | 2.0th),综合排名(0.0)
人A,年龄(60 | 0.0th),收入(55.0 | 1.5th),综合排名(0.0)
人B,年龄(45 | 0.0th),收入(50.0 | 1.0th),综合排名(0.0)
人C,年龄(20 | 0.0th),收入(50.0 | 1.0th),综合排名(0.0)
按年龄按升序排序的人员名单:
人C,年龄(20 | 2.5),收入(50.0 | 1.0th),综合排名(0.0)
人-E,年龄(30 | 2.0),收入(85.0 | 2.5th),综合排名(0.0)
人B,年龄(45 | 1.5),收入(50.0 | 1.0th),综合排名(0.0)
人D,年龄(55 | 1.0),收入(60.0 | 2.0th),综合排名(0.0)
人A,年龄(60 | 0.5th),收入(55.0 | 1.5th),综合排名(0.0)
按降序排列的排名首选项排序的人员列表:
人-E,年龄(30 |第2.0),收入(85.0 | 2.5th),综合排名(4.5)
人C,年龄(20 | 2.5),收入(50.0 | 1.0th),综合排名(3.5)
人D,年龄(55 | 1.0),收入(60.0 | 2.0th),综合排名(3)
人B,年龄(45 | 1.5),收入(50.0 | 1.0th),综合排名(2.5)
人A,年龄(60 | 0.5th),收入(55.0 | 1.5th),综合排名(2.5)
答案 0 :(得分:2)
您可以分别维护两个TreeSet来存储年龄和收入信息,这样您就可以轻松地从这两棵树中查询排序时的年龄和收入等级。
我们可以从TreeSet调用tailSet(int)
方法来获取大于或等于特定数字的数字列表,在这种情况下,它将是年龄/收入的等级。
TreeSet ageRank = new TreeSet();
TreeSet incomeRank = new TreeSet();
for(Person p : persons){
ageRank.add(p.getAge());
incomeRank.add(p.getIncome());
}
Collections.sort(persons, new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) {
int ageRank1 = ageRank.tailSet(p1.getAge()).size();
int ageRank2 = ageRank.tailSet(p2.getAge()).size();
int incomeRank1 = incomeRank.tailSet(p1.getIncome()).size();
int incomeRank2 = incomeRank.tailSet(p2.getIncome()).size();
//Calculate the combined_rank and return result here. Code omitted
}
});
使用这种方法,对于所有计算,一个用于循环的排序就足够了。
如果您需要定期更新人员列表,这种方法会派上用场,因为您不需要对年龄和收入进行排序,并且当有一个时候会一次又一次地重新计算所有排名更新,你只需要更新这两棵树。
注意:为了在用于排序的内部ageRank
类中使用incomeRank
和Comparator
,需要将它们声明为final或实例变量