Java:反向排序而不保持顺序

时间:2009-03-06 13:21:33

标签: java algorithm sorting comparison

更新:事实上,此问题唯一可接受的解决方案是将数组升序排序,然后将其反转。

S 成为以下事件序列:

Event | Time
  A   | 0:00
  B   | 0:01
  C   | 0:01
  D   | 0:02

我有一个简单的比较器来排序 S ,它根据 time 值对元素进行排序。

public int compare(Event e1, Event e2) {

    // Reverse sorting. 
    // _sortOrder is set outside this method.
    if(SORT_DESCENDING.equals(_sortOrder))
        return e2.getTime() - e1.getTime(); /* time is long */

    return e1.getTime() - e2.getTime();
}

问题是:当排序顺序升序时, S 正确排序:A,B,C,D。

但是当我使用反向排序时, S 变为D,B,C,A:

Event | Time
  D   | 0:02
  B   | 0:01 /* B and C should be reversed */
  C   | 0:01
  A   | 0:00

这是因为默认排序算法保留了具有相同 time 值的元素的原始顺序。

那么,如何在不保留原始订单的情况下对其进行排序?

注意:我知道我可以将 S 升序排序并进一步简单地还原它,但不幸的是,这不是我的选择。

8 个答案:

答案 0 :(得分:3)

你在用什么来排序?我猜这是保证它是stable sort的东西 - 就比较而言,你有两个相等的值。

在这种情况下,您可以施加任何额外的顺序吗?你能否轻松地告诉哪一个事件会在升序中排在第一位?如果是这样,只需使用原始比较,然后降序将开始自然地工作。

编辑:由于您的数据中没有任何额外的订购信息,如果在Oracle中再次执行相同的查询时以不同的顺序返回,我不会感到惊讶。但是,如果你只关心这个特定情况下的顺序,我建议你在Java内存表示中添加一个额外的字段来将原始索引存储在加载的列表中 - 当你读取数据时,记录多少你已经阅读并相应地分配了字段。然后你可以在排序时使用它。

答案 1 :(得分:3)

排序算法正确:0.01为0.01。除非你有什么东西没告诉我们。但是,如果您想要升序排序的完全相反顺序,则按升序排序并使用Collections.reverse()。我的意思是:

List<SomeObject> list = ...;
Collections.sort(list, myComparator);
Collections.reverse(list);

将为您提供您想要的内容。

但如果倒车不是一种选择,你只剩下两个选择:

  1. 不要让两个元素“平等”。包括另一个字段或添加合成字段(如当前索引或主键,如果它是一个数字)。这将为您提供可重现,一致和镜像的顺序;或
  2. 实施您自己的排序算法。不建议这样做,因为它太容易出错。
  3. 现在在你说你无法扭转之前(为什么?),让我问你如何排序?如果你正在使用Collections.sort()考虑源代码(Java 6u10):

    public static <T extends Comparable<? super T>> void sort(List<T> list) {
    Object[] a = list.toArray();
    Arrays.sort(a);
    ListIterator<T> i = list.listIterator();
    for (int j=0; j<a.length; j++) {
        i.next();
        i.set((T)a[j]);
    }
    }
    

    因此,它将集合复制到一个数组中,对该数组进行排序,然后使用它来重新排序集合。

    你确定无法承受逆转吗?

答案 2 :(得分:1)

我认为你真正想要的是二级排序标准,这样即使主排序标准认为项目相等,你也可以对项目进行排序。在您的示例中,主要标准是时间,辅助标准是“事件”。这样,在执行排序之前,您不必依赖于特定顺序的项目,这使事情变得更加容易。

如果你真的没有辅助标准,你可能需要研究稳定与不稳定的排序算法,如Jon Skeet所说。

答案 3 :(得分:1)

我错过了什么,或者你不能只使用事件名称(或A,B,C和D)作为辅助排序键吗?只需将您的比较器扩展为:

public int compare(Event e1, Event e2) {
    int cmp = e1.getTime() - e2.getTime(); // compare times
    if (cmp == 0)  // if times are equal, compare names instead
        cmp = e1.getName().compareTo(e2.getName());
    return SORT_DESCENDING.equals(_sortOrder)? -cmp : cmp;
}

答案 4 :(得分:1)

另一种方法但不解决您的问题:

List<SomeObject> list = ...;
Collections.sort(list, Collections.<SomeObject>reverseOrder());

答案 5 :(得分:0)

为什么0:01小于/大于0:01,或者为什么如果两者都相同,就应该以非常特殊的方式进行排序?

你不是在寻找反向排序,你正在寻找一种反向排序,对不起。 :)

答案 6 :(得分:0)

排序顺序看起来是正确的,只需比较e1.getTime()== e2.getTime()的名称。

答案 7 :(得分:0)

根据比较两个元素与切换它们的速度有多快,您可以只进行降序稳定排序,然后仅反转“相等”元素的所有区域。