注意DefaultMutableTreeNode的add方法

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

你可能感兴趣的:(java相关)