注意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<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值,所以就不会再出现问题。

时间: 2024-10-10 00:33:31

注意DefaultMutableTreeNode的add方法的相关文章

jquery的add()方法扩大选择返回

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <script type="text/javascript" src="jquery-1.11.3.min.js"></script> 6 <title>Title</title> 7 &

多线程对比linkedList和arrayList的add方法

上上一篇对linkedList和arrayList的源码对比:http://blog.csdn.net/aaashen/article/details/44925181 上一篇对linkedList和arrayList的各种方法进行单线程的对比:http://blog.csdn.net/aaashen/article/details/45011365 本篇用多线程对比,之对比add方法,插10000000条数据. 其中arrayList add 数据花费11615,linkedlist add数

在Vue的构造器里我们写一个add方法,然后我们用实例的方法调用它

html <div id="app"> <div>{{message}}</div> </div> js var vm = new Vue({ el:"#app", data:{ message:"HelloWorld" }, methods:{ add:function(a){ console.log(a) } } }) // 外部调用 vm.add("实例调用内部add方法"

thinkphp3.2.3 数据库写入add 方法的一些问题。

最近在做项目中遇到的一个数据操作add()方法,在不开启debug的模式下会漏掉一些字段没写入数据库. 当时并不知道是这个原因,明明在开发的时候都是没问题的,怎么突然出现这个问题,找了好久都没有头绪,实在没办法只好百度了. 后来在thinkphp的官方论坛上找到了一个解决的方法,说是因为缓存的问题,这时忽然想起了在上线的时候把debug模式关了,果断的去把debug模式打开试了一下 还真是这么回事.但是thinkphp官方并没有给什么解释.解决方法其实也很简单就是将对应应用下runtime目录下

关于 List add方法

add是将传入的参数作为当前List中的一个Item存储,即使你传入一个List也只会另当前的List增加1个元素addAll是传入一个List,将此List中的所有元素加入到当前List中,也就是当前List会增加的元素个数为传入的List的大小  eg: result.addAll(list);//把list中的每一个元素加到result中,result.size()==list.size() result.add(list);//将list作为一个元素加到result中,则result.s

TP add方法参数详解

tp add()方法参数详解: add($data='',$options=array(),$replace=false); 其中R$replace表示,是否添加数据时覆盖原有数据true表示覆盖,false表示不覆盖

java 17 - 6 TreeSet集合及其add()方法的源码解析

TreeSet:能够对元素按照某种规则进行排序. 排序有两种方式 A:自然排序 B:比较器排序 TreeSet集合的特点:排序和唯一 1 public class TreeSetDemo { 2 public static void main(String[] args) { 3 // 创建集合对象 4 // 自然顺序进行排序 5 TreeSet<Integer> ts = new TreeSet<Integer>(); 6 7 // 创建元素并添加 8 // 20,18,23,2

TreeSet集合的add()方法源码解析(01.Integer自然排序)

>TreeSet集合使用实例 >TreeSet集合的红黑树 存储与取出(图) >TreeSet的add()方法源码     TreeSet集合使用实例 package cn.itcast_05; import java.util.TreeSet; /* * TreeSet:能够对元素按照某种规则进行排序. * 排序有两种方式 * A:自然排序 * B:比较器排序 * * TreeSet集合的特点:排序和唯一 * * 通过观察TreeSet的add()方法,我们知道最终要看TreeMap的

注意ArrayAdapter的Add()方法

ArrayAdapter类可以作为ListView等的适配器资源,并且可以动态向适配器中添加新的数据,这就是ArrayAdapter.Add()方法的作用.但是在使用该方法时如果出错,那就需要检查ArrayAdapter构造方法中的参数T[] objects了,如果这个参数设置成了固定大小的数据类型的话,是不能完成添加的.这个Add()方法其实是在向这个参数中动态添加数据,而如果这个参数大小固定,添加时会出现异常. 举个例子: 1 //创建ListView对象 2 ListView mylv =