Java 将比较器与自定义比较器结合使用

Java Using Comparators in combination with custom Comparators

我想用我自己的自定义规则对当前仅包含字符串的以下示例列表进行排序。

ArrayList<String> coll = new ArrayList<>();
coll.add("just");
coll.add("sdsd");
coll.add("asb");
coll.add("b as");
coll.add("just");
coll.add("dhfga");
coll.add("jusht");
coll.add("ktsa");
coll.add("just");
coll.add("just");

我知道我可以为此编写自己的比较器,但据我所知,Java 也有比较器可以部分解决这个问题,我想知道如何使用 [=28] 中的比较器=] API 与我自己的组合。


应该如何排序?

单词 just 应始终是出现在列表中的第一个单词,然后是按字母顺序排列的所有其他单词。

Comparator.naturalOrder() 按字母顺序对列表进行排序,但是我如何将此比较器与自定义的比较器结合起来,检查单词是 just 还是其他。

你可以这样做:

coll.sort(Comparator
    .comparingInt((String s) -> s.equals("just") ? 0 : 1) // Words "just" first
    .thenComparing(Comparator.naturalOrder())); // Then others

第一步应该是定义自定义顺序。我会通过使用地图来做到这一点。

Map<String, Integer> orderMap = new HashMap<>();
int order = 0;
for(String specialWord : yourListOfSpecialWords){
    orderMap.put(specialWord, order++);
}

现在使用该地图和自然顺序作为备份构建比较器:

Comparator<String> comparator = ((Comparator<String>) (o1, o2) -> {
    int leftScore = orderMap.getOrDefault(o1, Integer.MAX_VALUE);
    int rightScore = orderMap.getOrDefault(o2, Integer.MAX_VALUE);
    return Integer.compare(leftScore, rightScore);
}).thenComparing(String::compareTo);

使用此比较器对您的列表进行排序。注意:您可能只想初始化地图一次并将其保存在常量中或至少在缓存中。

但是,如果您的特殊情况只是一个单词,正如您的更新建议的那样,那么这当然是矫枉过正,您应该在这里选择其他答案之一。

您可以将标准整合到比较器中,例如

coll.sort(Comparator.comparing((String s) -> !s.equals("just"))
                    .thenComparing(Comparator.naturalOrder()));

或者你分开操作,首先将所有出现的 "just" 移到前面,然后只对剩余的元素进行排序:

int howManyJust = 0;
for(int ix = 0, num = coll.size(); ix < num; ix++)
    if(coll.get(ix).equals("just") && ++howManyJust <= ix)
        Collections.swap(coll, ix, howManyJust-1);

coll.subList(howManyJust, coll.size()).sort(Comparator.naturalOrder());

虽然这看起来更复杂,但它可能更有效,尤其是对于较大的列表。