TreeSet 表现得很奇怪
我在使用 TreeSet (sortedNodes
) 和 ArrayList (nodes
) 时遇到了一个奇怪的问题。在我的程序中,我有一个从事件调度线程(来自 ActionListener
)调用的方法,这些行:
System.out.println("nodes: "+nodes.size());
sortedNodes.addAll(nodes);
System.out.println("sortedNodes: "+sortedNodes.size());
问题是,在某些集合上 sortedNodes.size()
返回的数字小于nodes.size()
(在这 3 行上,因此 nodes
的内容没有变化)。当我打印 sortedNodes
的内容时,除了不包含它应该包含的所有对象之外,它甚至没有排序。奇怪的是 - 如果我再次调用整个方法,它就会解决问题。我不明白 - 在相同的集合上执行相同的代码,但第一次它不起作用,第二次它起作用。 有什么想法吗?
编辑:如果我的问题不是很清楚,这应该有助于
exportTree();
exportTree();
pritns 的输出:
nodes: 7
sortedNodes: 4
b2[23,57]a[46,97]b[65,77]c[43,43]
nodes: 7
sortedNodes: 7
a[46,97]b[65,77]b1[55,89]b2[23,57]b3[20,20]c[43,43]c1[99,88]
比较器:
public class NodeComparator implements Comparator<Node>{
public int compare(Node o1, Node o2) {
return o1.getLabel().compareTo(o2.getLabel());
}
}
节点:
public class Node {
private int order;
private String label;
private Integer[] keys;
private Node[] pointers;
private Node parent;
public Node(int order, String label, Integer[] keys, Node[] pointers) {
this.order = order;
this.label = label;
this.parent = null;
if (pointers == null) {
this.pointers = new Node[order+1];
} else {
this.pointers = pointers;
}
if (keys == null) {
this.keys = new Integer[order];
} else {
this.keys = keys;
}
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public Integer[] getKeys() {
return keys;
}
public void setKeys(Integer[] keys) {
this.keys = keys;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public Node[] getPointers() {
return pointers;
}
public void setPointers(Node[] pointers) {
this.pointers = pointers;
}
public Node getPointer(int i) {
return pointers[i];
}
public void setPointer(int i, Node node) {
pointers[i] = node;
}
public Integer getKey(int i) {
return keys[i];
}
public void setKey(int i, Integer key) {
keys[i] = key;
}
}
整个方法:
public void exportTree() {
String graphInText = "";
if (nodeShapes.isEmpty()) {
graphInText = "empty";
} else {
char c = 'a';
ArrayList<Node> nodes = new ArrayList<Node>();
sortedNodes.clear();
System.out.println("nodeShapes: " + nodeShapes.size());
// populate the collection of nodes from their 2d representation(nodeShapes)
// and label every root with different character
for (NodeShape n : nodeShapes) {
nodes.add(n.getNode());
if (n.getParentLink() == null) {
n.getNode().setLabel(c + "");
c++;
}
}
System.out.println("nodes: " + nodes.size());
// copy all the nodes (currently every node except roots has label "0")
sortedNodes.addAll(nodes);
System.out.println("sortedNodes: " + sortedNodes.size());
// check labels of every node; if it contains any of the characters
// that were assigned to roots, use this label for every child of
// this node and add number of the pointer at the end of the string;
// when this is done, remove those nodes, which children have been
// labeled;
// repeat whole procedure while there is no node left - and this will
// happen, since every node is connected to a root or is a root;
while (!nodes.isEmpty()) {
ArrayList<Node> nodesToBeRemoved = new ArrayList<Node>();
for (Node n : nodes) {
for (char c2 = 'a'; c2 <= c; c2++) {
if (n.getLabel().contains(c2 + "")) {
for (int i = 1; i <= n.getOrder(); i++) {
Node child = n.getPointer(i);
if (child != null) {
child.setLabel(n.getLabel() + i);
}
}
nodesToBeRemoved.add(n);
}
}
}
if (!nodesToBeRemoved.isEmpty()) {
nodes.removeAll(nodesToBeRemoved);
}
}
Node[] nodesA = sortedNodes.toArray(new Node[sortedNodes.size()]);
for (Node n : nodesA) {
String nodeInText;
nodeInText = n.getLabel() + "[";
for (int i = 1; i < n.getOrder() - 1; i++) {
nodeInText += n.getKey(i) + ",";
}
nodeInText += n.getKey(n.getOrder() - 1) + "]";
graphInText += nodeInText;
}
}
System.out.println(graphInText);
label.setText(graphInText);
}
我还更改了程序,因此每次创建/删除 NodeShape 时,节点也会添加/删除到新集合中,并且我在exportTree()中使用这个新集合而不是nodeShapes - 但它的工作原理是相同的,因此nodeShapes没有问题。这只是 TreeSet.. 当我不使用它时,一切正常(但我没有对我的节点进行排序)。
I'm having a weird problem with TreeSet (sortedNodes
) and ArrayList (nodes
). In my program, I have in a method called from Event Dispatch Thread (from ActionListener
) these lines:
System.out.println("nodes: "+nodes.size());
sortedNodes.addAll(nodes);
System.out.println("sortedNodes: "+sortedNodes.size());
Problem is, on some collections sortedNodes.size()
returns lower number than nodes.size()
(on these 3 lines, so there is no change in content of nodes
). When I then print content of sortedNodes
, it's not even sorted in addition to not containing all the objects it should. Weird thing is - if I call the whole method again, it fixes the issue. I don't get it - same code is executed, on same collections, but first time it doesn't work and 2nd time it does.
Any ideas?
EDIT: If my problem is not very clear, this should help
exportTree();
exportTree();
pritns on output this:
nodes: 7
sortedNodes: 4
b2[23,57]a[46,97]b[65,77]c[43,43]
nodes: 7
sortedNodes: 7
a[46,97]b[65,77]b1[55,89]b2[23,57]b3[20,20]c[43,43]c1[99,88]
Comparator:
public class NodeComparator implements Comparator<Node>{
public int compare(Node o1, Node o2) {
return o1.getLabel().compareTo(o2.getLabel());
}
}
Node:
public class Node {
private int order;
private String label;
private Integer[] keys;
private Node[] pointers;
private Node parent;
public Node(int order, String label, Integer[] keys, Node[] pointers) {
this.order = order;
this.label = label;
this.parent = null;
if (pointers == null) {
this.pointers = new Node[order+1];
} else {
this.pointers = pointers;
}
if (keys == null) {
this.keys = new Integer[order];
} else {
this.keys = keys;
}
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public Integer[] getKeys() {
return keys;
}
public void setKeys(Integer[] keys) {
this.keys = keys;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public Node[] getPointers() {
return pointers;
}
public void setPointers(Node[] pointers) {
this.pointers = pointers;
}
public Node getPointer(int i) {
return pointers[i];
}
public void setPointer(int i, Node node) {
pointers[i] = node;
}
public Integer getKey(int i) {
return keys[i];
}
public void setKey(int i, Integer key) {
keys[i] = key;
}
}
Whole method:
public void exportTree() {
String graphInText = "";
if (nodeShapes.isEmpty()) {
graphInText = "empty";
} else {
char c = 'a';
ArrayList<Node> nodes = new ArrayList<Node>();
sortedNodes.clear();
System.out.println("nodeShapes: " + nodeShapes.size());
// populate the collection of nodes from their 2d representation(nodeShapes)
// and label every root with different character
for (NodeShape n : nodeShapes) {
nodes.add(n.getNode());
if (n.getParentLink() == null) {
n.getNode().setLabel(c + "");
c++;
}
}
System.out.println("nodes: " + nodes.size());
// copy all the nodes (currently every node except roots has label "0")
sortedNodes.addAll(nodes);
System.out.println("sortedNodes: " + sortedNodes.size());
// check labels of every node; if it contains any of the characters
// that were assigned to roots, use this label for every child of
// this node and add number of the pointer at the end of the string;
// when this is done, remove those nodes, which children have been
// labeled;
// repeat whole procedure while there is no node left - and this will
// happen, since every node is connected to a root or is a root;
while (!nodes.isEmpty()) {
ArrayList<Node> nodesToBeRemoved = new ArrayList<Node>();
for (Node n : nodes) {
for (char c2 = 'a'; c2 <= c; c2++) {
if (n.getLabel().contains(c2 + "")) {
for (int i = 1; i <= n.getOrder(); i++) {
Node child = n.getPointer(i);
if (child != null) {
child.setLabel(n.getLabel() + i);
}
}
nodesToBeRemoved.add(n);
}
}
}
if (!nodesToBeRemoved.isEmpty()) {
nodes.removeAll(nodesToBeRemoved);
}
}
Node[] nodesA = sortedNodes.toArray(new Node[sortedNodes.size()]);
for (Node n : nodesA) {
String nodeInText;
nodeInText = n.getLabel() + "[";
for (int i = 1; i < n.getOrder() - 1; i++) {
nodeInText += n.getKey(i) + ",";
}
nodeInText += n.getKey(n.getOrder() - 1) + "]";
graphInText += nodeInText;
}
}
System.out.println(graphInText);
label.setText(graphInText);
}
I also altered the program so every time I create/remove NodeShape, also Node is added/removed to new collection and I used this new collection in exportTree() instead of nodeShapes - but it works the same, so there is no problem with nodeShapes. It's just the TreeSet.. when I don't use it, everything works okay (but I don't get my nodes sorted).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
TreeSet 遵循 Set 语义,因此不允许重复。 ArrayList 确实允许重复,因此如果多次添加节点,它可能比 TreeSet 有更多的节点。
TreeSet 使用可比较语义进行排序。你的节点有可比性吗?你是否通过一个Comparator来告诉它如何排序?你应该。
对于基元、字符串和任何其他可比较的内容,TreeSet 可以自然地工作,无需您付出任何努力。
也许这些可以解释您所观察到的一些行为。
TreeSet follows Set semantics, so no duplicates are allowed. ArrayList does allow duplicates, so it could have more nodes than TreeSet if you add Nodes more than once.
TreeSet sorts using Comparable semantics. Is your Node Comparable? Do you pass a Comparator to tell it how to sort? You should.
TreeSet works naturally without any effort on your part for primitives, Strings, and anything else that is Comparable.
Perhaps those explain some of the behavior you're observing.
这就是为什么我要求提供代码...:) :) :)
您可能在将节点添加到集合之后但在打印出值之前更改节点的标签。标签用于设置等价性。一方面,由于您的节点提供了不一致的结果,因此您严重破坏了集合的内部工作......但这并不是真正的问题。
我怀疑正在发生的事情是,当将节点添加到集合中时,它们会直接从 ArrayList 中呈现“a”、“b”、“c”等标签...然后将它们纠正为“a1”, “b1”等标签随后(但在打印之前)。这就是第二个调用有效的原因,因为标签都已“固定”。在第一次调用中,它们第一次添加到集合中时可能确实是重复的。
...提供更好“控制”数据的早期调试例程将揭示这一点。
编辑以澄清:
您从包含七个节点的 ArrayList 开始,但其中 3 个与其他一些节点具有相同的标签,因此只有 4 个唯一标签。
您将所有 7 个元素添加到一个 Set 中,但该集合中仅获得 4 个元素,因为只有 4 个唯一标签。
然后修改节点的标签(例如更改“b”->“b1”)。
当您再次运行该方法时,一切都会正常,因为标签已经设置完毕。
关于早期“控制”调试的评论是建议在修改节点之前转储数组列表和集合的内容...即:在您看到奇怪的结果之后,而不是在更改“调试”的条件之后测试。
And this is why I asked for the code... :) :) :)
You are potentially changing the label of the node after adding it to the set but before printing out the values. The label is what is used for set equivalence. For one thing, you are badly mucking around the internal workings of the set since your nodes are providing inconsistent results... but that's not really the problem here.
What I suspect is happening is that the nodes are presenting labels like "a", "b", "c" right out of the ArrayList when adding them to the set... and then they are corrected to have their "a1", "b1", etc. labels afterwards (but before printing). This is why the second call works because the labels have all been "fixed". In the first call, they probably really are duplicates the first time they are added to the set.
...earlier debugging routines that provide better "control" data would reveal this.
Edit to clarify:
You start with an ArrayList of seven nodes but 3 of them have the same label as some of the others so that there are only 4 unique labels.
You add all 7 of those to a Set and only get 4 elements in the set because there were only 4 unique labels.
You then modify the labels of the nodes (for example changing "b" -> "b1").
When you run the method again then everything works because the labels have already been setup.
The comment about earlier "control" debugging was a suggestion to dump the contents of the array list and the set prior to modifying the nodes... ie: right after you see your strange results and not after altering the conditions of the "debug" testing.
您的行后面有很多代码:
在它下面所做的事情可能会修改您的节点对象,这会导致 exportTree() 方法第二次按预期运行。如果可以的话,注释掉这些行,看看您的方法在第二次调用时是否按预期工作。
You've got a lot of code after your line:
Something that's done beneath it may could be modifying your nodes object which causes the exportTree() method to behave as expected the second time around. If you can, comment out the lines and see if your method works as expected on the second call.
尽管我没有看到线程的痕迹,但这对我来说确实听起来像是一个线程问题。您是否尝试使用同步集合来检查行为是否相同?
Even though I see no traces of threading, this indeed sounds like a threading issue to me. Did you try using a Synchronized collection to check if the behaviour is the same?