由于本人在写一个小工具,用到了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值,所以就不会再出现问题。