Java比较器没有';不要比较每个物体

Java比较器没有';不要比较每个物体,java,comparator,Java,Comparator,我在使用Java比较器时遇到了一些麻烦。因此,我有一个父节点,它有一个子节点列表。我想对这些子对象进行排序,表面上看起来很简单,但是当比较器进行排序时,它只检查特定对象与特定对象,然后我假设它推断对象的位置,比如说如果a在d之前,f在d之后,b在d之前,这意味着b在f之前 下面是我当前设置的一个示例。我有一个父节点a,它有4个子节点b,e,l,g。当我对这些子节点进行排序时,我希望排序顺序是b,g,l,e,因此按字母表排序并始终确保父节点排在第一位 (请原谅这张拙劣的图纸) 非常简单,我有一个

我在使用Java
比较器时遇到了一些麻烦。因此,我有一个父节点,它有一个子节点列表。我想对这些子对象进行排序,表面上看起来很简单,但是当
比较器进行排序时,它只检查特定对象与特定对象,然后我假设它推断对象的位置,比如说如果
a
d
之前,
f
d
之后,
b
d
之前,这意味着
b
f
之前

下面是我当前设置的一个示例。我有一个父节点
a
,它有4个子节点
b
e
l
g
。当我对这些子节点进行排序时,我希望排序顺序是
b
g
l
e
,因此按字母表排序并始终确保父节点排在第一位

(请原谅这张拙劣的图纸)

非常简单,我有一个
节点
类,它包含一个ID,所以它是字母,然后是一系列子元素

public class Node {

    private char id;
    private Node parent;
    private List<Node> children = new ArrayList<>();


    public Node(char id) {
        this.id = id;
    }

    public void addChild(Node child) {
        this.children.add(child);
    }

    public List<Node> getChildren() {
        return children;
    }

    public char getId() {
        return id;
    }

    public void setParent(Node parent) {
        this.parent = parent;
    }

    public Node getParent() {
        return parent;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public boolean equals(Object obj) {
        return ((Node) obj).getId() == this.id;
    }
}
问题在于,排序完成后,它会检查:

E against B
G against E
L against G

所以它从不检查L和E,所以它无法知道哪个应该放在第一位。

您的订单违反了比较器的合同:

实现者还必须确保关系是可传递的:((compare(x,y)>0)&(compare(y,z)>0))意味着compare(x,z)>0

但是

compare('G','L')<0//因为字母表中'G'在'L'之前

由于您的
比较器
不是有效的
比较器
,因此可能会产生异常或意外结果。

好的排序算法会尽量减少比较次数,以获得良好的性能。这不是一个问题,而是这些排序算法的一个特点。如果将每个元素与其他元素进行比较,则不会更改结果


如果排序与预期不符,您应该仔细查看比较器。

它所做的比较是正确的,并且是预期的。如果L>G和G>E,那么根据比较器的规则,L>E

在这方面,比较器遵循正常的算术规则。3>2和2>1表示3>1(及物性)


你的问题可能是E是a的孩子。通常情况下,它不应该在这种树上。拥有多个父级会使树处理相当复杂。如果在进行比较时删除该链接,并使用某种递归算法遍历树,则应该可以使用。

正如前面指出的,您的比较器违反了比较器契约。。。但是,按照您所描述的方式进行您自己的排序不应该太复杂,您可能需要如下内容:

import java.util.Collections;
import java.util.List;

public class NodeSorter {

    public void sort(List<Node> nodes) {
        Collections.sort(nodes, (x, y) -> Character.compare(x.getId(), y.getId()));
        for (int i = 0; i < nodes.size() - 1; i++) {
            for (int j = i + 1; j < nodes.size(); j++) {
                Node x = nodes.get(i);
                Node y = nodes.get(j);
                if (y.getChildren().contains(x)) {
                    nodes.remove(y);
                    nodes.add(i, y);
                    i--;
                    break;
                }
            }
        }
    }
}

当排序把E放在G之前,G放在L之前,那么不可避免的是E也在L之前。你有什么问题?这是一个吗?在您的图形中,您有小写和大写节点,但在代码中,您似乎假定它们都是大写的。实际的节点ID都是大写的吗?我是否正确地理解了L是E的父节点,并且您总是希望父节点在其子节点之前?那么正确的排序是A-B-G-L-E吗?对不起,是的,所有大写@Eran我认为在这里使用比较器没有意义,因为不是所有节点都有直接的关系,这使得很难确保传递性。在我看来,您只需要修改宽度优先遍历。好的,谢谢,它只会产生意想不到的结果。那么,实现我自己排序的唯一解决方案是什么?@user3667111您可能必须这样做。我不确定是否有一个有效的
比较器可以满足您的要求。谢谢,但我不知道您删除链接是什么意思,如果我删除链接,它将完全破坏数据而不知道数据是什么,很难说您应该怎么做。
compare('G','E') > 0 // since 'G' comes after 'E' in the alphabet
compare('E','L') > 0 // since 'E' is a child of 'L'
compare('G','L') < 0 // since 'G' comes before 'L' in the alphabet
import java.util.Collections;
import java.util.List;

public class NodeSorter {

    public void sort(List<Node> nodes) {
        Collections.sort(nodes, (x, y) -> Character.compare(x.getId(), y.getId()));
        for (int i = 0; i < nodes.size() - 1; i++) {
            for (int j = i + 1; j < nodes.size(); j++) {
                Node x = nodes.get(i);
                Node y = nodes.get(j);
                if (y.getChildren().contains(x)) {
                    nodes.remove(y);
                    nodes.add(i, y);
                    i--;
                    break;
                }
            }
        }
    }
}
@Override
public int compare(Node o1, Node o2) {
    if (o1.getChildren().contains(o2)) {
        return -1;
    }

    if (o2.getChildren().contains(o1)) {
        return 1;
    }

    return Character.compare(o1.getId(), o2.getId());
}