【问题标题】:Java Comparator Arrays.sort()Java 比较器 Arrays.sort()
【发布时间】:2020-01-26 14:32:33
【问题描述】:

我想根据一些规则在Java中对二维数组的array进行排序,比如说到原点的距离。我看到了几种使用Arrays.sort()的方法:

1) Arrays.sort(points, Comparator.comparing(p -> p[0]*p[0] + p[1]*p[1]));

2)Arrays.sort(points, (p1, p2) -> p1[0]*p1[0] + p1[1]*p1[1] - p2[0]*p2[0] - p2[1]*p2[1]);

3) 定义class

class Point implements Comparable<Point>{
     // class variables, constructor
     public int compareTo(Point p) {
         return (x*x + y*y).compareTo(p.x*p.x + p.y*p.y);
     }
}

然后创建Points 类型的数组pts 并使用Arrays.sort(pts)。我的问题是关于 1)2):我看到了两者之间的区别,但我不明白什么时候使用一个,什么时候使用另一个,以及究竟是什么@987654330 @ 是在做。为什么第一个解决方案只用一个点携带信息,而第二个解决方案需要两个点?

【问题讨论】:

    标签: java arrays class comparator


    【解决方案1】:

    Comparator.comparing实现如下:

    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }
    

    即它使用您传递给它的Function 将每个比较的元素转换为Comparable,然后使用ComparablecompareTo 方法。

    当您传递函数p -&gt; p[0]*p[0] + p[1]*p[1] 时,您将每个点转换为其平方和。然后,当Comparator需要比较两个点时,比较两个点的平方和,这几乎相当于计算两个点平方和的差(并不完全等价,因为比较两个点如果数字溢出,通过计算它们的差异可能会产生错误的输出)。

    这正是你的第二个 Comparator - (p1, p2) -&gt; p1[0]*p1[0] + p1[1]*p1[1] - p2[0]*p2[0] - p2[1]*p2[1] - 所做的,

    p1[0]*p1[0] + p1[1]*p1[1] - p2[0]*p2[0] - p2[1]*p2[1] == (p1[0]*p1[0] + p1[1]*p1[1]) - (p2[0]*p2[0] + p2[1]*p2[1]).

    使用Comparator.comparing() 更安全,因为它比较两点的平方和而不计算它们的差。它使用DoublecompareTo() 代替(假设您的点的坐标是Doubledouble)。

    换句话说,第一个替代方案使用Function,它只需要一个点,因为此函数告诉Comprator.comparing 如何转换这两个点中的每一个。

    另一方面,第二种选择接受 2 个点(这是 Comparator.compare() 方法的必需参数)并确定这 2 个点的相对顺序。

    【讨论】:

      【解决方案2】:

      在 2) 中,必须对某个函数的比较的两个操作数进行比较。在 1) one 中,仅指定传递要比较的值的一个函数。除了comparing,还有用于数值的(如这里)comparingInt, comparingLong, comparingDouble

      所以 1) 是最容易阅读的。

      对于具有双倍斜边的点,可以使用到原点的实际距离

      Arrays.sort(points, Comparator.comparingDouble(p -> Math.hypot(p[0], p[1]));
      

      然后是坏消息:

      二维距离排序没有给出顺序。 因此,排序在两个元素之间不起作用。您只能针对一个固定点进行排序。说相对于某个固定点对数组点进行排名。 在这里,您获取到原点 (0, 0) 的距离。

      【讨论】:

        【解决方案3】:

        使用Comparator.comparing() 你是在说“我想在比较这个类的对象时使用这个属性”。该属性需要为Comparable,因此您会看到它经常与数字属性一起使用,例如:Comparator.comparing(MyObject::getId) 返回一个Comparator,其逻辑为“使用数字 id 比较 MyObjects”。

        使用(p1, p2) -&gt;,您可以自己做更多的工作。您有 2 个对象,您需要返回 -1、0 或 +1,具体取决于 p1 是否小于、等于或大于 p2。这允许更复杂的逻辑,但如果您基于一个或几个属性进行排序,则比第一个选项更冗长且可读性更低。

        最后你要让类本身实现Comparable。这通常被称为自然排序。 Point 并没有真正的 自然 排序,例如数字,因此我不建议创建 compareTo 方法。

        【讨论】:

          猜你喜欢
          • 2012-10-05
          • 1970-01-01
          • 2020-02-09
          • 1970-01-01
          • 2013-12-08
          • 2013-09-04
          • 1970-01-01
          • 2018-11-27
          • 1970-01-01
          相关资源
          最近更新 更多