使用多个键对Java对象进行排序

and*_*ras 50 java sorting comparable comparator

我有一个Duck对象的集合,我想用多个键对它们进行排序.

class Duck {
    DuckAge age; //implements Comparable
    DuckWeight weight; //implements Comparable
    String name;
}
List<Duck> ducks = Pond.getDucks();
Run Code Online (Sandbox Code Playgroud)

例如.我想主要根据他们的重量来排序,其次是他们的年龄.如果两只鸭子具有完全相同的重量和完全相同的年龄,那么让我们使用它们的名称作为三级键来区分它们.我可能会这样做:

Collections.sort(ducks, new Comparator<Duck>(){
    @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.name);
    }
});
Run Code Online (Sandbox Code Playgroud)

我经常这样做,但这个解决方案闻不到.它不能很好地扩展,并且很容易搞砸.当然必须有一种更好的方法来使用多个键对Ducks进行排序!有人知道更好的解决方案吗?

EDIT删除了不必要的else分支

JB *_*zet 51

番石榴更优雅:

return ComparisonChain.start()
     .compare(d1.weight, d2.weight)
     .compare(d1.age, d2.age)
     .compare(d1.name, d2.name)
     .result();
Run Code Online (Sandbox Code Playgroud)

Apache commons-lang有一个类似的结构CompareToBuilder.

  • Apache的CompareToBuilder更加优雅,因为它默认使用空值优先处理空值.除非为每个.compare()调用添加第三个参数(Ordering.natural(..nullsFirst()),否则Guava的ComparisonChain将抛出NullPointerException. (3认同)
  • 如果,你知道,你喜欢零. (2认同)

Bor*_*vić 20

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();
  }
});
Run Code Online (Sandbox Code Playgroud)


Nox*_*lle 14

首先,您的解决方案并不那么慢.

如果你真的想要另一种方法,那就给每只鸭子一个"得分",这个数字基本上是一个数字,它是三个特征的总和,但是对于体重有一个巨大的权重(借口几乎不可避免的双关语),对于年龄来说是一个较小的权重; 这个名字很小.

您可以为每个特征分配~10位,因此对于每个特征,您必须在该范围内0..1023.

score = ( (weight << 10) + age) << 10 + name;
Run Code Online (Sandbox Code Playgroud)

这可能是完全不需要的,但无论如何:)


and*_*ras 13

Java 8解决方案:

Comparator<Duck> cmp = Comparator.comparing(Duck::getWeight)
    .thenComparing(Duck::getAge)
    .thenComparing(Duck::getName);
Run Code Online (Sandbox Code Playgroud)

讨厌lambda,方法引用和默认方法:)!太糟糕了,我们必须定义getter,或使用显式lambda,如下所示:

Comparator<Duck> cmp = Comparator
    .comparing((Duck duck)-> duck.weight)
    .thenComparing((Duck duck)-> duck.age)
    .thenComparing(duck-> duck.name);
Run Code Online (Sandbox Code Playgroud)

类型推断不适用于隐式lambdas,因此您必须指定前两个lambda的参数类型.Brian Goetz这个答案中提供了更多细节.


Thi*_*ler 6

您可以使用Apache Commons Lang的CompareToBuilder .(它解释了可比性,但也适用于比较器).