当前位置: 首页 > 知识库问答 >
问题:

按多个字段对Java豆进行排序的正确方法

张毅
2023-03-14

我们有带有复杂比较器的代码,这些代码已用于在整个应用程序中对 java 对象进行排序。从历史上看,这些方法都是有效的,但是自从在Java 7中引入TimSort以来,我们偶尔会发现比较方法违反了其总合同!错误。。取决于对象中保存的数据。

这是我们的传统比较器之一的示例(可能已有近十年的历史 - 请原谅狡猾):

 public int compare(TemplateBean b1, TemplateBean b2) {

  // avoid null pointer exceptions
  if (b1 == null && b2 == null) return 0;
  if (b1 == null) return 1;
  if (b2 == null) return -1;

  int cmp = 0;
  if ("UNATTACHED".equals(b1.getStatusCode()) &&
     !"UNATTACHED".equals(b2.getStatusCode())) {
     cmp = 1;
  }
  if (!"UNATTACHED".equals(b1.getStatusCode()) &&
     "UNATTACHED".equals(b2.getStatusCode())) {
     cmp = -1;
  }
  if (!"UNATTACHED".equals(b1.getStatusCode()) &&
     !"UNATTACHED".equals(b2.getStatusCode()) &&
     !"FIELDSIMPLE".equals(b1.getRefRltshpTypeCode()) &&
     !"FIELDSIMPLE".equals(b2.getRefRltshpTypeCode()) &&
     !"CUSTOM".equals(b1.getRefRltshpTypeCode()) &&
     !"CUSTOM".equals(b2.getRefRltshpTypeCode()) &&
     !"FUNCTION".equals(b1.getRefRltshpTypeCode()) &&
     !"FUNCTION".equals(b2.getRefRltshpTypeCode())) {
     String parent1 = b1.getGroupCode() == null ? "" : b1.getGroupCode().toUpperCase();
     String parent2 = b2.getGroupCode() == null ? "" : b2.getGroupCode().toUpperCase();
     cmp = parent1.compareTo(parent2);
  }

  if (cmp == 0) {
     Integer i1 = b1.getSortOrder() == null ? Const.ZERO : b1.getSortOrder();
     Integer i2 = b2.getSortOrder() == null ? Const.ZERO : b2.getSortOrder();
     cmp = i1.compareTo(i2);
  }

  if (cmp == 0) {
     String s1 = b1.getShortDescription();
     if (s1 == null) s1 = "";
     String s2 = b2.getShortDescription();
     if (s2 == null) s2 = "";
     cmp = s1.compareToIgnoreCase(s2);
  }

  return cmp;  }

所以,我想复制这个功能,但是使用一个可以安全地与TimSort一起使用的比较器。

从代码中您可以看到此比较有多个级别。

  1. 它将比较组码
  2. 如果组码相同,则会比较排序顺序
  3. 如果排序顺序相同,则将比较描述

这意味着它将返回特定级别的比较结果。这可能是两个字符串或两个整数的比较结果。我认为这就是打破时间排序的原因。

我能够让这个比较器解决通用合同问题的唯一方法是散列bean的内容并执行字符串比较。其他想法包括编写我们自己的排序函数…肯定有更好的方法吗?

是否应该以另一种方式构建豆子来支持这一点?

共有2个答案

夏侯俊美
2023-03-14

@RomanKonovai给出的答案是正确的,但添加了更多细节。

考虑代码如何比较这三个对象,并假设所有非引用:

               A          B          C
Status         UNATTACHED UNATTACHED UNATTACHED
RefRltshpType  CUSTOM     FUNCTION   CUSTOM
Group          Cat        Ball       Apple
SortOrder      10         20         30

通过问题中的实现,我们可以看到A

解决这个问题的方法是这样做:

private int objectCompare(String allowed, Comparable v1, Comparable v2) {
  if (v1 == v2) return 0;
  if (v1 == null) return 1;
  if (v2 == null) return -1;
  boolean c1 = v1.equals(allowed);
  boolean c2 = v2.equals(allowed);
  return c1 ? c2 ? 0 : 1 : c2 ? -1 : 0;
}
private int objectCompare(Comparable v1, Comparable v2) {
  if (v1 == v2) return 0;
  if (v1 == null) return 1;
  if (v2 == null) return -1;
  return v1.compare(v2);
}
public int compare(TemplateBean b1, TemplateBean b2) {

  // avoid null pointer exceptions
  if (b1 == b2) return 0;
  if (b1 == null) return 1;
  if (b2 == null) return -1;

  int cmp = objectCompare("UNATTACHED", b1.getStatusCode(), b2.getStatusCode());
  if (cmp == 0) {
    cmp = objectCompare("FIELDSIMPLE", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
    if (cmp == 0) {
      cmp = objectCompare("CUSTOM", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
      if (cmp == 0) {
        cmp = objectCompare("FUNCTION", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
        if (cmp == 0) {
          cmp = objectCompare(b1.getGroupCode(), b2.getGroupCode());
          if (cmp == 0) {
            cmp = objectCompare(b1.getSortOrder(), b2.getSortOrder());
            if (cmp == 0) {
              cmp = objectCompare(b1.getShortDescription(), b2.getShortDescription());
            }
          }
        }
      }
    }
  }

  return cmp;
}
狄楷
2023-03-14

上述比较器的主要问题是它不是传递的。它似乎在较旧的JDK上“工作”,因为它们没有提供对损坏的比较器的检测,但它在一般情况下无法正常工作,直到JDK 7才发现错误行为。

其非传递性的来源是group pCode属性上的条件比较。考虑比较器将对象A和B排序为A时的情况

要解决此问题,应始终考虑< code>groupCode,并且由于< code > refltshptypecode 值而跳过< code>groupCode的每个对象应被视为小于< code>groupCode现在用于比较的任何对象。

比较方法应该看起来像(这只是给你一个想法):

public int compare(TemplateBean b1, TemplateBean b2) {

    // avoid null pointer exceptions
    if (b1 == null && b2 == null) return 0;
    if (b1 == null) return 1;
    if (b2 == null) return -1;

    int cmp = 0;
    if ("UNATTACHED".equals(b1.getStatusCode()) &&
       !"UNATTACHED".equals(b2.getStatusCode())) {
        cmp = 1;
    }
    if (!"UNATTACHED".equals(b1.getStatusCode()) &&
       "UNATTACHED".equals(b2.getStatusCode())) {
       cmp = -1;
    }

    if (shouldBeComparenByGroupCode(b1) != shouldBeComparedByGroupCode(b2)) {
        if (!shouldBeComparenByGroupCode(b1)) {
            return -1;
        } else {
           return 1;
        }
    }

    if (shouldBeComparenByGroupCode(b1) && shouldBeComparenByGroupCode(b2)) {
        String parent1 = b1.getGroupCode() == null ? "" : b1.getGroupCode().toUpperCase();
        String parent2 = b2.getGroupCode() == null ? "" : b2.getGroupCode().toUpperCase();
        cmp = parent1.compareTo(parent2);
    }

    if (cmp == 0) {
        Integer i1 = b1.getSortOrder() == null ? Const.ZERO : b1.getSortOrder();
        Integer i2 = b2.getSortOrder() == null ? Const.ZERO : b2.getSortOrder();
        cmp = i1.compareTo(i2);
    }

    if (cmp == 0) {
        String s1 = b1.getShortDescription();
        if (s1 == null) s1 = "";
        String s2 = b2.getShortDescription();
        if (s2 == null) s2 = "";
        cmp = s1.compareToIgnoreCase(s2);
    }

    return cmp;
}

在哪里

private static boolean shouldBeComparenByGroupCode(TemplateBean b1) {
     return !"UNATTACHED".equals(b1.getStatusCode()) &&
            !"FIELDSIMPLE".equals(b1.getRefRltshpTypeCode()) &&
            !"CUSTOM".equals(b1.getRefRltshpTypeCode()) &&
            !"FUNCTION".equals(b1.getRefRltshpTypeCode());
}
 类似资料:
  • 问题内容: 我有一个Java对象列表,希望根据多个字段进行排序。 是否可以使用或接口根据多个字段对列表进行排序?我看到的所有示例仅根据一个领域进行排序。换句话说,可以按“校园”或“教师”或“建筑”进行排序。我想按“校园”,“教师”,“建筑”(因为它在SQL中存在)进行排序 问题答案: 您的比较器如下所示: 基本上,只要到目前为止已比较的属性相等(),它就会继续比较类的每个连续属性。

  • 问题内容: 我有一个从排序的csv创建的以下列表 我实际上想按两个条件对列表进行排序:首先按字段1中的值,然后按字段2中的值。我该怎么做? 问题答案: 像这样:

  • 问题内容: 我有这样的txt文件中的数据列表 我的任务是按照每个标准对这些数据进行排序,例如)按日期,纬度和经度排序 我尝试像这样的泡沫排序 这可行,但是需要太多时间 那里有在txt文件数据 有没有其他方法可以对这些数据进行排序? 问题答案: 我可能会破坏一些学生的家庭作业,但是这里… 正如课题所建议的那样,Java的自然方法是创建一个表示您的数据的类。然后实现一个将传递给实用方法的方法。 在运行

  • 问题内容: 在Chrome 14和Firefox 5(尚未测试其他浏览器)中,以下代码无法正确对数字进行排序: 返回10,100,20,30,60 我尝试了不同的数字,它总是像没有出现0一样,否则会正确地对数字进行排序。有人知道为什么吗? 问题答案: 我尝试了不同的数字,它总是像没有出现0一样,否则会正确地对数字进行排序。有人知道为什么吗? 您正在按字典顺序进行排序(例如,将对象转换为字符串,然后

  • 问题内容: 我有这样的数组 如何按return_fare asc和one_way_fare asc排序值? 我尝试了array_multisort()但最终却得到了混乱的数据。 Asort仅适用于一维数组,我需要按两个或多个值进行排序,我如何才能像SQL中那样实现此功能,按field1 asc,field2 asc排序? 问题答案: 是正确的函数,您必须以某种方式搞砸了: 如果您在PHP的手册页上

  • 问题内容: 如何按其字段之一(例如或)对对象数组进行排序? 问题答案: 使用usort,这是从手册改编而成的示例: 您还可以将任何callable用作第二个参数。这里有些例子: 使用匿名函数(来自PHP 5.3) 从班级内部 使用箭头函数(来自PHP 7.4) 另外,如果要比较数字值,则应使用“比较”功能。