Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Java对父项和子项进行排序_Java_Sorting_Comparator - Fatal编程技术网

使用Java对父项和子项进行排序

使用Java对父项和子项进行排序,java,sorting,comparator,Java,Sorting,Comparator,我有一个“Item”类,它包含以下字段(简而言之):id(与SQL Server上Item表的主键相关)、description、sequence(非null整数)和link(对父对象id的引用)可以为null 我想使用Java进行如下排序: Id Sequence Link Description 1 1 null Item A 99 ..1 1 Son of A, first of the sequence 57

我有一个“Item”类,它包含以下字段(简而言之):id(与SQL Server上Item表的主键相关)、description、sequence(非null整数)和link(对父对象id的引用)可以为null

我想使用Java进行如下排序:

Id    Sequence   Link    Description
1     1          null    Item A
99    ..1        1       Son of A, first of the sequence
57    ..2        1       Son of A, second of the sequence
66    ..3        1       Son of A, third of the sequence
2     2          null    Item B
3     3          null    Item C
...
(Item it) -> it.getLink() == null ? it.getSequence() : idSeqMap.get(it.getLink())
(为了更好的可视化,我把这些点放进去)

也就是说,我希望某个项目的子项直接位于其父项之下,按“序列”字段排序

我尝试使用比较器,但失败了:

public class SequenceComparator implements Comparator<Item> {
    @Override
    public int compare(Item o1, Item o2) {
        String x1 = o1.getSequence().toString();
        String x2 = o2.getSequence().toString();
        int sComp = x1.compareTo(x2);

        if (sComp != 0) {
            return sComp;
        } else {
            x1 = o1.getLink().toString();
            x2 = o2.getLink() == null?"":o2.getLink().toString();
            return x1.compareTo(x2);
        }
    }
}
公共类SequenceComparator实现Comparator{
@凌驾
公共整数比较(项目o1、项目o2){
字符串x1=o1.getSequence().toString();
字符串x2=o2.getSequence().toString();
int sComp=x1。与(x2)相比;
如果(sComp!=0){
返回sComp;
}否则{
x1=o1.getLink().toString();
x2=o2.getLink()==null?”:o2.getLink().toString();
返回x1.compareTo(x2);
}
}
}

我该怎么做呢?

考虑到您的数据结构是一棵树(根节点为
null
),没有循环:

在找到共同的祖先之前,你必须在树上走到
o1
o2
。一旦您这样做了,请沿两个分支后退一步,以找到它们的相对顺序(使用
序列


找到共同祖先可能很棘手,我不知道在线性时间内是否可能,但在O(n logn)时间内(n为分支长度)肯定可能找到新答案:我认为您不需要一个比较器来控制完整的排序,因为在排序子代时,您需要父代的序列,在比较器中,您无法轻松或自然地访问它

相反,我建议分几个步骤进行排序:

  • 按父项将项目分组。因此,一个组将是id为1的项及其所有子项。没有子项的项目将单独在一个组中
  • 对每个组进行排序,使父组先出现,然后按正确顺序排列所有子组
  • 按父级的顺序对组进行排序
  • 将已排序的组连接到一个列表中
  • 像这样,使用Java8流和
    List.sort()

    (此输出是通过
    toString
    方法生成的,该方法在将具有父项的项目转换为
    字符串时打印点)

    如果您不能使用Java8,我仍然相信上面提到的步骤的总体思路是可行的,只有一些步骤需要更多的代码

    我删除了我以前的答案,因为我误解了
    getLink()
    返回的内容,然后认为这个答案不值得挽救

    编辑:

    实际上,我忽略了
    收集器的文档中的这一部分。groupingBy()
    :“没有关于返回的…
    列表
    对象的…可变性的保证。”它仍然适用于我的Java 8。如果列表的不变性会阻止排序,那么解决方案是创建一个新的
    ArrayList
    ,其中包含相同的项

    多亏了斯图尔特·马克斯的灵感,排序内部列表的比较器不必像上面那样笨拙。排序可以用以下压缩方式编写:

            innerList.sort(Comparator.comparing(itm -> itm.getLink() == null ? null : itm.getSequence(),
                    Comparator.nullsFirst(Integer::compare)));
    

    鉴于层次结构中只有两层,这可以归结为一种经典的多级排序。根据
    链接
    字段是否为空,有两种项目,即父项和子项。诀窍在于,每一级的排序并不在特定字段上。相反,排序所依据的值取决于项目的类型

    排序的第一级应在父值上。父项的父值是其序列,但子项的父值是其链接到的父项的序列。子项通过其id链接到父项,因此我们需要做的第一件事是建立从id到父节点序列值的映射:

        Map<Integer, Integer> idSeqMap =
            list.stream()
                .filter(it -> it.getLink() == null)
                .collect(Collectors.toMap(Item::getId, Item::getSequence));
    
    第二级排序应在子值上。父项的子值为null,因此需要在任何非null值之前对null进行排序。子项的子值是其序列。用于获取子值的lambda表达式为:

    (Item it) -> it.getLink() == null ? null : it.getSequence()
    
    现在,我们可以使用Java8中引入的
    Comparator
    helper函数来组合这些函数。结果可以直接传递到
    List.sort()
    方法

    list.sort(Comparator.comparingInt((Item it) -> it.getLink() == null ? it.getSequence() : idSeqMap.get(it.getLink()))
                        .thenComparing((Item it) -> it.getLink() == null ? null : it.getSequence(),
                                       Comparator.nullsFirst(Integer::compare))
                        .thenComparingInt(Item::getId));
    
    第一级排序非常简单;只需将第一个lambda表达式(提取父值)传递给
    Comparator.comparingit

    第二级排序有点棘手。我假设
    getLink()
    的结果是一个可为空的
    整数。首先,我们必须使用第二个lambda表达式提取子值。这会产生一个可为空的值,因此如果我们将其传递给
    然后进行比较
    ,我们将得到一个
    NullPointerException
    。相反,
    然后比较
    允许我们传递一个辅助比较器。我们将使用它来处理空值。对于这个二级比较器,我们通过

        Comparator.nullsFirst(Integer::compare)
    
    这将比较
    Integer
    对象,首先排序空值,然后使用
    Integer.compare
    方法依次比较非空值

    list.sort(Comparator.comparingInt((Item it) -> it.getLink() == null ? it.getSequence() : idSeqMap.get(it.getLink()))
                        .thenComparing((Item it) -> it.getLink() == null ? null : it.getSequence(),
                                       Comparator.nullsFirst(Integer::compare))
                        .thenComparingInt(Item::getId));
    

    最后,我们比较id值作为最后手段。如果您仅将此比较器用于排序,则此选项是可选的;重复项将彼此相邻。但是,如果将此比较器用于
    树集
    ,则需要确保不同的项之间的比较永远不会相等。假设数据库id值足以区分所有唯一项。

    为什么不直接在SQL中对其排序?事实上,这会容易得多,因为您正在尝试对数字上的项目进行排序。这是一个。你在找。我需要用Java做这个。这不是我的决定
        Comparator.nullsFirst(Integer::compare)