在Java

时间:2015-05-26 03:34:51

标签: java algorithm sorting

我正在寻找在Java中使用多个字段进行加权排序的有效实现。此问题与How to provide most relevant results with Multiple Factor Weighted SortingNeed help maximizing 3 factors in multiple, similar objects and ordering appropriately类似。但是,我要求就有效实施提出一些指导。

在下面的示例中,Person类包含ageincome字段,我希望将persons与较低ageincome进行排序基于给定首选项和降序排列的更高age组合。我为incomepackage 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)

1 个答案:

答案 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类中使用incomeRankComparator,需要将它们声明为final或实例变量