Sorting Java objects using multiple keys

馋奶兔 提交于 2019-11-26 10:25:53
JB Nizet

Guava is more elegant:

return ComparisonChain.start()
     .compare(d1.weight, d2.weight)
     .compare(d1.age, d2.age)
     .compare(d1.name, d2.name)
     .result();

Apache commons-lang has a similar construct, CompareToBuilder.

List<Duck> ducks = new ArrayList<Duck>();
Collections.sort(ducks, new Comparator<Duck>() {

  @Override
  public int compare(Duck o1, Duck o2) {

    return new org.apache.commons.lang.builder.CompareToBuilder().
        append(o1.weight, o2.weight).
        append(o1.age, o2.age).
        append(o1.name, o2.name).
        toComparison();
  }
});
Noxville

Firstly, your solution isn't that slow.

If you really want another method, then give each duck a "score" which is essentially a single number that is the sum of their three characteristics, but with a huge weighting (excuse the almost unavoidable pun) for weight, a lesser one for age; and a very small one for the name.

You can allocate ~10 bits for each characteristic, so for each characteristic you have to be in the range 0..1023.

score = ( (weight << 10) + age) << 10 + name;

This is probably completely unneeded, but whatever :)

andras

Java 8 solution:

Comparator<Duck> cmp = Comparator.comparing(Duck::getWeight)
    .thenComparing(Duck::getAge)
    .thenComparing(Duck::getName);

Hooray for lambdas, method references, and default methods:)! Too bad we have to define getters, or use explicit lambdas, like so:

Comparator<Duck> cmp = Comparator
    .comparing((Duck duck)-> duck.weight)
    .thenComparing((Duck duck)-> duck.age)
    .thenComparing(duck-> duck.name);

Type inference won't work with implicit lambdas, so you have to specify the argument type of the first two lambdas. More details in this answer by Brian Goetz.

You can use the CompareToBuilder from Apache Commons Lang. (It explains comparable, but works for Comparator too).

You can use chained BeanComparators from Commons BeanUtils:

Comparator comparator = new BeanComparator("weight", new BeanComparator("age"));

http://commons.apache.org/beanutils/v1.8.3/apidocs/org/apache/commons/beanutils/BeanComparator.html

I have just rewritten your code without nested else statements. Do you like it now?

@Override
public int compare(Duck d1, Duck d2){
    int weightCmp = d1.weight.compareTo(d2.weight);
    if (weightCmp != 0) {
        return weightCmp;
    }
    int ageCmp = d1.age.compareTo(d2.age);
    if (ageCmp != 0) {
        return ageCmp;
    } 

    return d1.name.compareTo(d2.age);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!