由于本人在写一个小工具,用到了swing的JTree组件,节点实现类为DefaultMutableTreeNode,却遇到了一个有点想不通的问题,后来看源代码找到了答案,谨此记录一下。下面是一个对此问题设计的一段代码:
public static void main(String[] args) {
DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");//根节点
DefaultMutableTreeNode persons = new DefaultMutableTreeNode("persons");//二级节点,其下有四个叶子节点
//叶子节点
persons.add(new DefaultMutableTreeNode("zhangsan"));
persons.add(new DefaultMutableTreeNode("lisi"));
persons.add(new DefaultMutableTreeNode("wangwu"));
persons.add(new DefaultMutableTreeNode("zhaoliu"));
root.add(persons);
//例如现在有这么一种需求,在删除二级节点persons后,将其下的子节点放置到其父节点上,在这也是root节点下
//于是有了下面的代码
Enumeration<DefaultMutableTreeNode> enumeration = persons.children();
while(enumeration.hasMoreElements()) {
DefaultMutableTreeNode next = enumeration.nextElement();
System.out.println(next);//只是为了方便查看添加了多少子节点,结果却是只添加了两次,只添加到了zhangsan和wangwu
root.add(next);
}
persons.removeFromParent();//将persons节点从root子节点中移除
//输出结果为2,root只有两个子节点
System.out.println(root.getChildCount());
}
奇怪的地方在于,persons节点下明明有4个子节点,为什么迭代过程中却只添加了两个呢?答案就在DefaultMutableTreeNode的add方法中,来看看源码:
public void add(MutableTreeNode newChild) {
if(newChild != null && newChild.getParent() == this)
insert(newChild, getChildCount() - 1);
else
insert(newChild, getChildCount());
}
该方法中,获取到要插入的索引后,调用insert方法:
public void insert(MutableTreeNode newChild, int childIndex) {
if (!allowsChildren) {
throw new IllegalStateException("node does not allow children");
} else if (newChild == null) {
throw new IllegalArgumentException("new child is null");
} else if (isNodeAncestor(newChild)) {
throw new IllegalArgumentException("new child is an ancestor");
}
MutableTreeNode oldParent = (MutableTreeNode)newChild.getParent();
//注意这里,判断要添加的子节点是否有父节点,如果有,则从父节点中移除该子节点
//而上述的例子中正好是原来有父节点的情况
if (oldParent != null) {
oldParent.remove(newChild);
}
newChild.setParent(this);
if (children == null) {
children = new Vector();
}
children.insertElementAt(newChild, childIndex);
}
下面是remove方法源码:
public void remove(MutableTreeNode aChild) {
if (aChild == null) {
throw new IllegalArgumentException("argument is null");
}
if (!isNodeChild(aChild)) {
throw new IllegalArgumentException("argument is not a child");
}
remove(getIndex(aChild)); // linear search
}
public void remove(int childIndex) {
MutableTreeNode child = (MutableTreeNode)getChildAt(childIndex);
children.removeElementAt(childIndex);//调用移除元素方法
child.setParent(null);
}
在DefaultMutableTreeNode中用一个Vector对象来保存子节点列表,而在Vector的removeElementAt方法中更新了elementCount成员变量,也就是在Vector中还有多少个元素:
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;//elementCount自减1
elementData[elementCount] = null; /* to let gc do its work */
}
现在再去看看DefaultMutableTreeNode的children方法:
public Enumeration children() {
if (children == null) {
return EMPTY_ENUMERATION;
} else {
return children.elements();//调用Vector的elements()方法
}
}
public Enumeration<E> elements() {
return new Enumeration<E>() {
int count = 0;
public boolean hasMoreElements() {
return count < elementCount;//而判断是否还有下一个元素时就要依赖elementCount的值
}
public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return elementData(count++);
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}
到这里应该就能明白为什么了,root添加第一个节点zhangsan后,zhangsan节点从persons中移除了,那么lisi节点成为persons中的索引为0的节点。由于Vector更新了elementCount,elementCount值变了3,Enumeration成员count值为1,所以在root添加Enumeration返回的下一个节点时获取到的是wangwu(索引为1),count值变为2,elementCount值也变为了2;再到下一次调用hasMoreElements方法时就返回了false,故造成了上述现象。
上述问题的解决办法是调用java.util.Collections的list方法将Enumeration转为一个List对象,由于在list方法之前没有调用DefaultMutableTreeNode的add方法,所以List对象中的元素数量是正确的,这之后遍历List时调用DefaultMutableTreeNode的add方法已经不再依赖Vector的elementCount值,所以就不会再出现问题。