向比较器的compare方法添加其他规则

时间:2019-01-16 19:46:48

标签: java android comparator

我目前有一个代码段,该代码段以升序返回列表的字符串:

df2 <- df %>%
group_by(Date = as.Date(datetime)) %>%
mutate(Fn = F[hour(datetime) == 0]), 
z = (Fn - F)/Fn) %>%
ungroup() %>%
select(-Date)

虽然可行,但我想向订单中添加一些自定义“规则”,以便将某些字符串放在最前面。例如:

Collections.sort(myList, new Comparator<MyClass>() {
    @Override
    public int compare(MyClass o1, MyClass o2) {
        return o1.aString.compareTo(o2.aString);
    }
});

是否可以使用这样的if(aString.equals("Hi")){ // put string first in the order } if(aString begins with a null character, e.g. " ") { // put string after Hi, but before the other strings } // So the order could be: Hi, _string, a_string, b_string, c_string 自定义列表的排序?

3 个答案:

答案 0 :(得分:3)

有可能。

使用Java 8功能

您可以将函数传递给Comparator.comparing方法以定义您的规则。请注意,我们只返回整数,这是应该排在最前面的元素的最低整数。

Comparator<MyClass> myRules = Comparator.comparing(t -> {
    if (t.aString.equals("Hi")) {
        return 0;
    }
    if (t.aString.startsWith(" ")) {
        return 1;
    }
    else {
        return 2;
    }
});

如果您希望其余元素按字母顺序排序,那么如果您的类实现了thenComparing(Comparator.naturalOrder()),则可以使用Comparable。否则,您应该首先提取排序键:

Collections.sort(myList, myRules.thenComparing(Comparator.comparing(t -> t.aString)));

请注意,返回的实际特定数字并不重要,重要的是排序时,较低的数字位于较高的数字之前,因此,如果始终将字符串“ Hi”放在首位,则对应的数字应该是返回的最低数字(在我的情况下为0)。

使用Java <= 7个功能(兼容Android API 21级)

如果您无法使用Java 8功能,则可以这样实现:

Comparator<MyClass> myRules = new Comparator<MyClass>() {

    @Override
    public int compare(MyClass o1, MyClass o2) {
        int order = Integer.compare(getOrder(o1), getOrder(o2));
        return (order != 0 ? order : o1.aString.compareTo(o2.aString));
    }

    private int getOrder(MyClass m) {
        if (m.aString.equals("Hi")) {
            return 0;
        }
        else if (m.aString.startsWith(" ")) {
            return 1;
        }
        else {
            return 2;
        }
    }
};

并这样称呼它:

Collections.sort(list, myRules);

其工作原理如下:首先,两个接收到的字符串都映射到您的自定义规则集,并相互减去。如果两者不同,则操作getOrder(o1) - getOrder(o2)确定比较。否则,如果两者都相同,则将按照人脑学顺序进行比较。

Here is some code in action

答案 1 :(得分:3)

MC Emperor的answer非常好(+1),因为它满足了OP不使用Java 8 API的要求。它还使用整洁的内部函数技术(getOrder方法)将条件映射到较小的整数值,以进行第一级比较。

这是使用Java 8构造的替代方法。假设MyClass有一个getString方法可以完成明显的工作。

    Collections.sort(myList,
        Comparator.comparing((MyClass mc) -> ! mc.getString().equals("Hi"))
                  .thenComparing(mc -> ! mc.getString().startsWith(" "))
                  .thenComparing(MyClass::getString));

这是不透明的,直到您习惯了这种样式。关键的见解是,提供给Comparator.comparingComparator.thenComparing的“提取器”功能通常只是提取一个字段,但是它可以是对任何其他值的一般映射。如果该值是可比较的,则不需要提供其他比较器。在这种情况下,提取器函数是布尔表达式。这被装箱成一个布尔值,事实证明它是可比较的。由于falsetrue之前排序,因此我们需要对布尔表达式求反。

还请注意,我必须为lambda参数提供一个显式的类型声明,因为类型推断通常不适用于诸如此类的链式比较器情况。

答案 2 :(得分:0)

是的,有可能,您可以完全控制compareTo()方法。两件事:

  1. 使用String#equals代替==来比较字符串
  2. 确保在特殊情况下,都选中两个参数compareTo。

一种实现某些事物的具体方式,其中某些单词总是最先出现而某些单词总是最末尾,并且在例外之间定义了顺序:

Map<String, Integer> exceptionMap = new HashMap<>();
exceptionMap.put("lowest", -2);
exceptionMap.put("second_lowest", -1);
exceptionMap.put("second_highest", 1);
exceptionMap.put("highest", 2);

public int compareToWithExceptionMap(String s1, String s2) {
  int firstExceptional = exceptionMap.getOrDefault(s1, 0);
  int secondExceptional = exceptionMap.getOrDefault(s2, 0);

  if (firstExceptional == 0 && secondExceptional == 0) {
    return s1.compareTo(s2);
  }
  return firstExceptional - secondExceptional;
}