【问题标题】:Java Using Comparators in combination with custom ComparatorsJava 将比较器与自定义比较器结合使用
【发布时间】:2018-10-31 11:06:40
【问题描述】:

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

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 也有比较器可以部分解决这个问题,我想知道如何结合使用 Java API 中的比较器 和我自己的。


应该如何排序?

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

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

【问题讨论】:

  • 是否要排除重复的“just”字?
  • 我不想排除他们。

标签: java sorting collections java-8 comparator


【解决方案1】:

你可以这样做:

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

【讨论】:

  • BooleanComparable,所以这里不需要处理int的值:coll.sort(Comparator.comparing((String s) -&gt; !s.equals("just")) .thenComparing(Comparator.naturalOrder()));
  • @Holger 对。甚至更短:coll.sort(null); coll.sort(Comparator.comparing("just"::equals).reversed());
  • 是的,排序操作是稳定的,null 表示“自然顺序”,但我会避免利用它;执行两个排序操作可能效率不高,并且使用null 作为比较器会破坏泛型类型系统,因为您不会在编译时检查元素是否实际可比。所以我会继续使用带有单个sort 的复合比较器。或者做“将"just"移到前面”而不像my answer那样进行排序...
  • @Holger null 部分可以用Collections.sort(coll) 修复。
  • 或者你留在coll.sort(Comparator.naturalOrder()));
【解决方案2】:

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

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);

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

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

【讨论】:

  • 使用Comparator.comparingInt((String o) -&gt; orderMap.getOrDefault(o, Integer.MAX_VALUE))可以避免代码重复。此外,当您只使用orderMap.put(specialWord, orderMap.size()); 时,您不需要int order 变量。
【解决方案3】:

您可以将标准集成到比较器中,例如

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());

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-10
    • 2018-12-22
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-20
    • 2021-09-23
    相关资源
    最近更新 更多