【问题标题】:How to write a function using natural order and comparators如何使用自然顺序和比较器编写函数
【发布时间】:2018-01-19 08:40:20
【问题描述】:

我是 Java 新手,我正在尝试实现一种可能使用自然顺序或给定比较器参数的排序算法。

假设我有一个可比较的类C,具有一些自然顺序compareTo,一个比较器子类ByAltOrder,我有一个Comparator<C> 方法,它返回一个新的ByAltOrder。

public class C implements Comparable<C> {
        ...                                     //fields and constructor
    public int compareTo(C that) { ... }        //natural order
    public Comparator<C> altOrder() {           //some alternative order
        return new ByAltOrder();
    }
    public class ByAltOrder implements Comparator<C>{
        public int compare(C x, C y) { ... }
    }
}

我想编写一个可以使用自然顺序或替代顺序的函数。我知道如何编写一个使用 either 自然顺序或替代顺序的函数,但我不想两次编写相同的代码,但差异很小。这可能吗?

假设我想编写一个返回非空数组最大值的函数。按照自然顺序,它看起来像这样:

public C max(C[] xs){
    res = xs[0];
    for (i = 1; i < xs.length; i++) {
        if (xs[i].compareTo(res) > 0) {
            res = points[i];
        }
    }
    return res;
}

使用 Comparator 看起来像这样,我可以将 x.altOrder() 作为第二个参数传递:

public C max(C[] xs, Comparator c){
    res = xs[0];
    for (i = 1; i < xs.length; i++) {
        if (c.compare(xs[i], res) > 0) {
            res = points[i];
        }
    }
    return res;
}

但是我如何编写一个包含两者的函数呢?

编辑:我知道在 Java 中有一个 Arrays.sort,但实现 sort 或 max 只是我关于传递比较器/代码重用/Java 实践的问题的一个玩具示例。也许我没有说清楚。

【问题讨论】:

  • 您的第一个方法应该简单地委托给第二个方法并将 Comparator.naturalOrder() 作为第二个参数传递。请不要使用原始类型。

标签: java code-reuse


【解决方案1】:

如果你真的想避免代码重复,你根本不应该实现那个操作。

Comparator<C> altOrder = …;
C[] array = …;
C naturalMax = Collections.max(Arrays.asList(array));// natural order
C altMax     = Collections.max(Arrays.asList(array), altOrder); // alternative order

请注意,Arrays.asList 不会复制,而只会包装数组,因此没有性能考虑阻止使用这些方法。

顺便说一下,ByAltOrder 和 altOrder() 都应该是 static,因为它们不依赖于 C 的特定实例。

如果您想将这些方法作为练习来实现,其他答案已经指向它,请使用委托和Comparator.naturalOrder()。

如果您使用的不是 Java 8 或更新版本,则此内置比较器不可用,但您可以使用 Collections.reverseOrder(Collections.reverseOrder()) 解决它以获得具有相同行为的比较器,但是,当然,您应该将其替换为Comparator.naturalOrder() 迁移到 Java 8 或更高版本后立即使用。

【讨论】:

  • 感谢您的回答。只有一条评论:“顺便说一下,ByAltOrder 和 altOrder() 都应该是静态的,因为它们不依赖于特定的 C 实例。”这取决于altOrder。例如altOrder(x, y)比较x到this和y到this的距离,那么这取决于C的具体实例。
  • @Y.Pei 确实,可能存在包含来自特定 C 实例的状态的用例。从问题中不清楚您是否有这样的用例。但即便如此,我还是只保留altOrder() 作为实例方法,仍然将ByAltOrder 实现为static(或顶级)类,但让它在构造函数中接收必要的值。这样,它只保留必要的值,而不是对另一个 C 实例的引用。此外,无论以后是否修改 C 实例,它都能确保一致的行为。
【解决方案2】:

从技术上讲,JB Nizet 是对的。
够了:

public C max(C[] xs){
    return max(xs, Comparator.naturalOrder());
}

public C max(C[] xs, Comparator<? super C> c){
    C res = xs[0];
    for (int i = 1; i < xs.length; i++) {
        if (c.compare(xs[i], res) > 0) {
            res = points[i];
        }
    }
    return res;
}

现在,具体来说,编写这些处理似乎无关紧要
要查找数组的最大元素(以 Comparable/Comparator 表示),您可以使用 Arrays.stream().max :

C max = Arrays.stream(xs).max(c); 

或

C max =  Arrays.stream(xs).max(new ByAltOrder());

如果数组为空,则通过处理 null 的情况(感谢亲爱的 Holger),您的方法可以很简单:

public C findMax(C[] xs){
   return findMax(xs, Comparator.naturalOrder());
}

public C findMax(C[] xs, Comparator<? super C> c){
    return Arrays.stream(xs).max(c).orElse(null);
}

【讨论】:

    【解决方案3】:

    你可以这样做:

    public <C extends Comparable<? super C>> C max(C[] xs){
        return max(xs, Comparator.naturalOrder());
    }
    
    public <C> C max(C[] xs, Comparator<C> c){
        C res = xs[0];
        for (C x: xs) {
            if (c.compare(x, res) > 0) {
                res = x;
            }
        }
        return res;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2018-10-09
      • 2010-09-24
      • 2019-03-19
      • 2021-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多