android 向Comparator的compare方法添加附加规则

to94eoyn  于 2023-01-19  发布在  Android
关注(0)|答案(3)|浏览(442)

我现在有一个代码片段,它以升序返回列表中的字符串:

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

是否可以像这样使用Comparator自定义列表的排序?

ubby3x7f

ubby3x7f1#

MC Emperor的answer非常好(+1),因为它满足了OP不使用Java 8 API的要求。它还使用了一种简洁的内部函数技术(getOrder方法),将条件Map到小整数值,以实现第一级比较。
这里有一个使用Java8构造的替代方法,它假设MyClass有一个getString方法,该方法执行显而易见的操作。

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

在您习惯这种风格之前,这是非常不透明的,关键的一点是,提供给Comparator.comparingComparator.thenComparing的“extractor”函数通常只是提取一个字段,但是它可以是到任何其他值的一般Map。如果那个值是Comparable,那么就不需要为它提供额外的Comparator。在这种情况下,提取器函数是一个布尔表达式。这被装箱为一个布尔值,结果是Comparable。由于false的顺序在true之前,我们需要对布尔表达式求反。
还要注意,我必须为lambda参数提供一个显式的类型声明,因为类型推断通常不适用于像这样的链式比较器情况。

y1aodyip

y1aodyip2#

有可能。

使用Java 8功能

你可以传递一个函数给Comparator.comparing方法来定义你的规则,注意我们只返回整数,最小的整数代表应该先出现的元素。

Comparator<MyClass> myRules = Comparator.comparing(t -> {
    if (t.aString.equals("Hi")) {
        return 0;
    }
    else 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);

其工作原理如下:首先,将收到的两个字符串Map到您的自定义规则集,然后相减。如果两者不同,则运算Integer.compare(getOrder(o1), getOrder(o2)) 1确定比较。否则,如果两者相同,则使用词法顺序进行比较。
Here is some code in action.
1始终使用Integer::compare,而不是用一个减去另一个,因为整数溢出可能导致错误结果。See here

ndasle7k

ndasle7k3#

是的,这是可能的,您可以完全控制compareTo()方法。
1.使用String#equals而不是==来比较字符串
1.确保检查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;
}

相关问题