Java 数组排序列表与列表排序列表

Coz*_*ama 7 java sorting list comparator

我有一个点列表,其中每个都是一个很小的​​size列表2。我想按 的升序对点列表进行排序x,如果x值相等,我通过按 的降序排序来打破平局y

我编写了一个自定义比较器来对点进行排序,如下所示:

Collections.sort(points, (a, b) -> {
    if (a.get(0) != b.get(0)) {
        return a.get(0) - b.get(0);
    } return b.get(1) - a.get(1); 
});
Run Code Online (Sandbox Code Playgroud)

这是排序之前的输入

(2, 1000)
(9, -1000)
(3, 15)
(9, -15)
(5, 12)
(12, -12)
(5, 10)
(10001, -10)
(19, 8)
(10001, -8)
Run Code Online (Sandbox Code Playgroud)

这是使用上述比较器排序后产生的结果:

(2, 1000)
(3, 15)
(5, 12)
(5, 10)
(9, -15)
(9, -1000)
(12, -12)
(19, 8)
(10001, -10)
(10001, -8)
Run Code Online (Sandbox Code Playgroud)

观察结果:

  1. 输入在 上按升序排序x
  2. (5, 12)之前已正确放置(5, 10)
  3. (9, -15)之前已正确放置(9, -1000)
  4. 不过,(10001, -10)是放在之前的(10001, -8)。尽管-8大于-10.

感觉我错过了一些微不足道的东西。我尝试了一些其他编写比较器的方法,例如使用Integer.compare(a, b)或 just a.compareTo(t),但得到了相同的结果。

最后,我将点的表示形式从 改为List<Integer>int[]并再次编写了相同的比较器。参见下面的结果:

Collections.sort(points, (a, b) -> {
    if (a[0] != b[0])
        return a[0] - b[0];
    return b[1] - a[1];
});
Run Code Online (Sandbox Code Playgroud)

排序前输入:

(2, 1000)
(9, -1000)
(3, 15)
(9, -150
(5, 12)
(12, -12)
(5, 10)
(10001, -10)
(19, 8)
(10001, -8)
Run Code Online (Sandbox Code Playgroud)

排序后:

(2, 1000)
(3, 15)
(5, 12)
(5, 10)
(9, -15)
(9, -1000)
(12, -12)
(19, 8)
(10001, -8)
(10001, -10)
Run Code Online (Sandbox Code Playgroud)

因此,数组列表已正确排序,正如(10001, -8)之前正确放置的那样(10001, -10)

我无法理解为什么改变点的表示解决了这个问题,因此解决了这个问题。如果需要,我可以提供有关如何创建点列表的更多详细信息。

Ale*_*nko 7

我错过了一些微不足道的事情

equals()应该使用方法来进行对象比较。Double equals==检查两个引用是否指向内存中的同一对象

如果您更改比较器内的条件,!a.get(0).equals(b.get(0))它将正常工作。

然而,(10001, -10)被放在(10001, -8)之前。即使-8大于-10

出现这种行为的原因是JVM缓存了范围内的Integer(以及ByteShort和)的所有实例。即这些实例被重用,假设值为 的自动装箱结果将始终是相同的对象。Long[-128; 127]int12

由于示例中的小值(例如3, 5, )12将由单个对象表示,因此对它们进行比较==没有问题。但是,与值为will 的==两个实例进行比较的结果是,因为在这种情况下,堆中将有两个不同的对象Integer10001false

缓存常用对象的方法称为享元设计模式它在Java中很少使用,因为当创建和销毁大量相同的对象时,这种模式可以带来好处。只有在这种情况下,缓存这些对象才能显着提高性能。据我所知,它用于游戏开发。

利用物体的力量

Point必须是一个对象,而不是一个列表,正如Code-Apprentice在他的回答中指出的那样。利用对象的力量,不要过度使用集合。它带来了几个优点

  • 类为您提供了一种结构,当您从对象的角度思考时,可以更轻松地组织代码;
  • 在类中声明的行为是可重用的并且更容易测试;
  • 通过类,您可以使用多态性的力量。

注意:对象也可能被滥用,其中一个可能的指标是,一个类除了 getters 之外没有声明任何行为,并且它的数据正在该类外部的代码中以某种方式进行处理。

尽管点的概念(作为几何对象)并不复杂,但在方法方面有一些有用的选项。例如,您可以使该类的实例Point能够检查它们是否水平垂直对齐,或者两个点是否在特定半径内。并且Point类可以实现Comparable接口,以便点能够在没有Comparator.

排序

Java 8方法sort() 被添加到接口中List。它需要一个 的实例Comparator,并且如果列表的元素实现可比较,并且您希望它们根据自然顺序排序null可以作为参数传递。

如果指定的比较器为 null,则此列表中的所有元素都必须实现 Comparable 接口,并且应使用元素的自然排序。

因此,Collections您可以sort()直接在点列表上调用方法,而不是使用实用程序类(假设Point实现Comparable<Point>):

points.sort(null); // the same as   points.sort(Comparator.naturalOrder()); 
Run Code Online (Sandbox Code Playgroud)

此外,您还可以使用default接口static中的方法Comparator(例如ComparisonInt()thenComparing() )创建多个自定义比较器。

有关如何使用Java 8方法构建比较器的更多信息,请查看本教程


Cod*_*ice 5

@Alexander Ivanchenko很好地解释了为什么你会看到你所做的行为。该问题的一种解决方案是使用Integer.compareTo()

Collections.sort(points, (a, b) -> {
    int xCompare = a[0].compareTo(b[0];
    if (xCompare != 0) {
        return xCompare;
    }
    retirm a[1].compareTo(b[1];
});
Run Code Online (Sandbox Code Playgroud)

除此之外,我建议创建一个Point类:

class Point {
    public int x;
    public int y;
}
Run Code Online (Sandbox Code Playgroud)

这里我使用public字段来遵循结构模式。如果您想添加 getter 和 setter 以及其他行为,请随意这样做。使用类来表示数据的结构使代码更易于理解且更易于维护。事实上,您可以轻松地Comparable在一个Point或一个Comparator<Point>类上实现。

  • @daniu `Comparable` 不是遗产。但它只能由真正具有“自然顺序”的类来实现。点没有自然顺序;人们可以选择很多不同的策略,因此这些策略确实应该使用“比较器”来实现。 (3认同)
  • 我同意,点必须明确地由 **类** 表示,而不是列表。也许最好让这些点来决定如何比较它们。我的意思是实现“Comparable” (2认同)
  • 我有点考虑实施“Comparable”遗产。我倾向于创建公共静态最终“Comparator”(例如“Point.BY_X”),它可以在几乎所有相同的上下文中使用,但更能表达其实际行为。 (2认同)