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