使用Guava Ordering对对象列表进行多标准排序

13

我有一个类,无法实现comparable接口,但是需要基于2个字段进行排序。我该如何使用Guava实现?

假设该类为:

class X {
  String stringValue;
  java.util.Date dateValue;
} 

我有一个这样的列表:

List<X> lotsOfX;

我希望按照'value'字段的值进行排序,然后在每个'value'字段的“组”中按照'dateValue'字段的降序排序。

到目前为止我一直在做的是:

List<X> sortedList = ImmutableList.copyOf(Ordering.natural().onResultOf(dateValueSortFunction).reverse().sortedCopy(lotsOfX));
sortedList = ImmutableList.copyOf(Ordering.natural().onResultOf(stringValueSortFunction).sortedCopy(sortedList));

这些函数的定义如下:

public class DateValueSortFunction<X> implements Function<X, Long> {

    @Override
      public Long apply(X input) {
        return input.getDateValue().getTime();  //returns millis time
      }
}

并且:

public class StringValueSortFunction<X> implements Function<X, Integer> {

      @Override
        public Integer apply(X input) {
          if(input.getStringValue().equalsIgnoreCase("Something"))
            return 0;
          else if(input.getStringValue().equalsIgnoreCase("Something else"))
            return 1;
          else
            return 2;
        }
}

sortedList的期望输出是:

Something   03/18/2013
Something   03/17/2013
Something else  03/20/2013
Something else  03/19/2013
....

我的方法有效,但显然在两次遍历列表时效率低下。有更好的方法吗?

我在使用 GWT 应用程序中。实现 Comparable 不是一个选择。

3个回答

24

我猜你想要的是Ordering.compound。你可以用一条语句完成所有操作,但我会使用:

Ordering<X> primary = Ordering.natural().onResultOf(stringValueSortFunction);
Ordering<X> secondary = Ordering.natural()
                              .onResultOf(dateValueSortFunction)
                              .reverse();
Ordering<X> compound = primary.compound(secondary);

List<X> sortedList = compound.immutableSortedCopy(lotsOfX);

天啊!谢谢你,这个帮我省了很多的IF...ELSE语句!我本来要陷入交叉条件的噩梦中。 - Ethenyl

17

一种功能较弱但可能更加简洁的解决方案:

new Ordering<X>() {
  public int compare(X x1, X x2) {
    return ComparisonChain.start()
      .compare(x1.stringValue, x2.stringValue)
      .compare(x2.dateValue, x1.dateValue) // flipped for reverse order
      .result();
  }
}.immutableSortedCopy(listOfXs);

我确实研究过这个问题,但我读到.compare需要输入实现可比较的内容。所以如果我理解正确,在这种情况下它会按字母顺序排序字符串?但是我需要放入自己的自定义排序函数。 - user949110
如果您想使用自定义比较器对字符串进行排序,那么您可以使用compare(x1.stringValue, x2.stringValue, new MyCustomStringComparator()) - Louis Wasserman
我明白了。只要它们的表现不相上下,我对两者都没问题。我认为另一个答案也很干净利落。 - user949110
4
对于那些认为交换参数以获取相反顺序过于微妙的人,你可以将比较器作为第三个参数传递,并使用 Collections.reverseOrder() ,然后读作:.compare(x1.dateValue, x2.dateValue, reverseOrder())(假设你正在使用静态导入)。 - whiskeysierra

2

Java 8提供了Comparator上的方法,可以简洁地指定链接比较器。与新引入的List.sort一起使用,您可以执行以下操作:

lotsOfX.sort(
    Comparator.comparingInt(x -> stringValueSortFunction.apply(x.stringValue))
        .thenComparing(x -> x.dateValue, Comparator.reverseOrder()));

当然,这会改变列表的内容,如果你想保留原始列表不变,请先复制一份;或者,如果你想要一个不可变的副本,可以将比较器包装在Ordering中并使用immutableSortedCopy


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接