网络中树型菜单实现方法及其效率研究
来源:互联网 发布:多益网络 招股说明书 编辑:程序博客网 时间:2024/05/24 00:08
网络中树型菜单实现方法及其效率研究
陈航
北京林业大学信息学院 计算机系 100083
摘要:对于树型菜单大家已经见得很多了,无论是软件中树菜单的制作还是基于WEB的树型菜单的应用,这个看似很简单的菜单在具体实现和效率方面有很大的差别,对于节点数量小,节点层数小的树型菜单来说我们用各种方法实现的结果基本差不多,但是对于层树很多,节点数量很大,关系复杂的树壮菜单而言各种实现办法之间是相差很大的,本文基于几种常用的实现方法来研究其实现方法和效率的问题。
关键字:树型菜单 效率
前言:对于网页中的树状菜单其实现结构大概可以分为以下几种结构:根节点,分支节点,叶节点,树支,对于内层结构可以分为:静态代码实现方式,异步载入模式,而异步载入模式中又可以分为:总体树载入模式和单节点载入模式,对于异步载入模式主要是出于对数据库的操作。同时对于构造树的算法方面大致最常用的也就是:父子节点编号法,和前缀码编号法,而节点的传输方面又可以分为:服务器端动态生成HTML方式,服务器端静态生成HTML方式,客户端保存HTML方式,可以看到这里实现方法很多,也很复杂,所以本文仅仅研究了几种比较成功的例子,虽然部分代码和程序来源于网络,但我还是具体实现了一种利用APPLET方式生成动态树,并且是单节点刷新的方法,而且能重用,具体实现思想和设计模式将在本文最后的实例分析中可以看到。
正文:
1.理论分析:
1.1语言实现方法分析:各种实现方法之间由于所在的平台不同可能表现出的优越性不同,但是其本质,也就是编译方式和解释方式决定了其效率的高低,下面我们比较几种实现方式的具体差异,从最低层来看看他们本应该存在的差异。
运行原理:作为一种脚本语言,用于实现HTML网页中的动态操作,其运行原理和一般程序不一样,而是在程序运行的过程中被逐行解释的,同时它是以事件驱动的方式完成对事件的处理。
特点及其局限性:跨平台可以说是它最大的特点,给它带来优点的同时也带来了很多局限性,比如:游览器的局限性和安全性有关的局限性,但是因为它和HTML很好的结合模式,从而也导致了它是应用很广的一门脚本语言。
运行原理:属于嵌入到游览器环境中的程序,必须由游览器的虚拟机(JVM)负责执行。当在本地编译完成以后,生成字节码文件,这样我们就可以通过导入字节码文件的方式,来实现我们的操作。
特点及其局限性:和Javascript一样也有垮平台性,但是我们只需要对其编译完以后用户就可以很方便的实现其功能,同时因为是java的一种嵌入式开发模式,我们可以很方便的利用SWING组件和其中的类,这样会给我来很高的效率。
1.2数据加载模式分析:具体各以分为一次性加载数据模式和异步加载模式。
1.3节点信息传输问题:在浏览器里显示的树结构其实都是一个个 HTML 元素组合起来的,在 WEB 页面里的树都是根据树节点的信息组合成一串的 HTML 元素列来显示,这一步从节点信息到 HTML 的转化可以在两个地方生成:一个是在服务器端,一个是在客户端。
1.4树结构实现算法分析:
ID号
父节点ID
1
1
2
1
3
1
4
3
5
1
6
5
搜索成的树型图:(图
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
搜索结果不同:(图
+ 1 + 1
+ 2 + 3
+ 3 + 2
+ 4 + 4
+ 5 + 5
+ 6 + 6
没有互换之前的搜索兔 互换后的搜索图
*说明:我们把节点号为2和3的节点在数据库中互换位置,得到不同的结果,虽然这样一个节点的子节点个数和相关信息没有变化,但是对于自身在父节点中的位置也发生了变化,对于某些需要具体顺序的应用将产生很大影响。
编码例子:
ID
0
0.1
0.2
0.3
搜索结果:
+ 0
+ 0.1
+ 0.2
+
+ 0.3
+
2.具体实现方法:
由于涉及的语言方面的实现方法很多,本文只对网上比较流行的实现方法HTML结合javascript的方法还有java-applet实现方法进行了分析,同时对于各种软件制作树状菜单的原理也进行了分析。
2.1 HTML-javascript实现方法:我们利用HTML完成对节点信息的描叙,这完全是静态的添加HTML代码的操作,同时用利用javascript脚本来实现对节点的事件处理。
<!DOCTYPE HTML PUBLIC "-//W
<html>
<head>
<title></title>
<link type="text/css" href="css/tree.css" rel="stylesheet">
<script language=javascript>
function ChangeStatus(o)
{
if (o.parentNode)
{
if (o.parentNode.className == "Opened")
{
o.parentNode.className = "Closed";
}
else
{
o.parentNode.className = "Opened";
}
}
}
</script>
</head>
<body>
<div class="TreeMenu" id="CategoryTree">
<h4>CSS树形菜单</h4>
<ul>
<li class="Opened"><img class=s src="css/s.gif" onclick="javascript:ChangeStatus(this);"><a href="#">根节点</a>
<ul>
<li class="Opened"><img class=s src="css/s.gif" onclick="javascript:ChangeStatus(this);"><a href="#">我的文档</a>
<ul>
<li class="Opened"><img class=s src="css/s.gif" onclick="javascript:ChangeStatus(this);"><a href="#">JavaScript</a>
<ul>
<li class="Child"><img class=s src="css/s.gif"><a href="#">1</a></li>
<li class="Child"><img class=s src="css/s.gif"><a href="#">2</a></li>
<li class="Child"><img class=s src="css/s.gif"><a href="#">3</a></li>
<li class="Child"><img class=s src="css/s.gif"><a href="#">4</a></li>
<li class="Child"><img class=s src="css/s.gif"><a href="#">5</a></li>
<li class="Child"><img class=s src="css/s.gif"><a href="#">6</a></li>
<li class="Child"><img class=s src="css/s.gif"><a href="#">生成node</a></li>
</ul></li>
</ul>
</div>
</body>
</html>
<liclass="Opened"><imgclass=ssrc="css/s.gif"onclick="javascript:ChangeStatus(this);"><a href="#">根节点</a> 进行控制,对于每个分支节点我们都需要添加这条,对于各个父节点的子节点的数量我们必须通过添加如下代码实现,<li class="Child"><img class=s src="css/s.gif"><a href="#">1</a></li>,这样我们就可以实现HTML的静态显示,用javascript来实现事件控制,对于图片加载还需要说明的是,我们看到的节点的图片是加载进去的,同时对于连线的效果也是加载进去的,这也是这次研究的一个发现,对于这个代码如何处理事件是利用函数ChangeStatus来控制的。
2.2 HTML-javascript动态加载实现方法:(说明:这个实例是引用网友“meizz”提供的MzTreeView1.0,其控件被CSDN论坛进行修改,而作为其论坛的树状导航菜单)这里的动态加载实现方法事实上就是从数据库中获得相关的节点信息,再构造出树结构。
A.采用数据一次性加载:把获得的树信息一次性加载给用户,减轻了服务器的负担和访问次数。
B.节点信息的压缩传输:节约了在传送节点信息的HTML文件的时候文件过于庞大。
C.数据库设计:采用父子节点编码方法,结构简单,清晰,同时为每个节点添加了对应的脚本操作,这样节省了打开各个节点时的时间。
D.异步展示:对于整个节点的HTML文件的生成不是以整体的方式展示在用户面前,而是以单单要显示的页面而生成其HTML。
E.采用文字树线:这样避免了对于图片树线的加载,使得展开速度更加快。
F.控件的扩展性:控件的扩展性和重用性都比较高。
G.其他优点由于本人知识范围有限无法获得。
2.3 java-applet实现方法(对应于文件夹代码三):对于applet这种嵌入式开发模式我们可以利用java自身包含大量很方便的类和接口完成生成树的操作。本实例子是通过对比meizz网友的设计模式重新开发的一种,动态实现单节点刷新的树状菜单生成方法,主要用到了JTree接口和相关的类。
(*说明:本程序全是通过本人编写而成,目的是对比不同的数据库设计方法,和不同的加载模式对树型菜单效率的影响,之所以采用这些模式,主要是和上面的一个实例进行对比。)
A.采用前缀码编码方式,属性为NodeID,并且为主键。
B.采用限制搜索次数的限制,考虑到形成树的时候如果采取递归搜索方式,这样就会造成效率比较低,所以为每个节点设置了另外一个属性Childnum,目的是加快数据库的搜索速度,如果不用这种方式,数据库扫描搜索的次数将扫描所有的节点,这样我们只需要扫描Childnum次就可以得到子节点的信息。
C.采用前缀码搜索,以父节点的ID号作为每次搜索字符串的前缀,我们只需要再添加一个‘.’符号并改变响应的尾数,这样我们就可以得到子节点的信息。
D.利用异步刷新模式,动态的载入用户点击的节点的子节点信息。
E.采用判断文件夹型节点设计方法,对于我们的节点可以分为根节点,和分支节点我把其Folder属性设置为1,这样我们就可以判断得到这个节点是否是根节点或者是分支节点,如果是我们就加载子节点,如果不是就不用加载,进一不减少了对数据库的操作。
F.设置节点是否已打开过属性,当我们发现一个节点的Opened值为0时,我们就需要对起节点添加节点,如果为1,说明我们不需要在加载子节点了,当然这样的操作前已经对其Folder值进行了判断,这样我们又节省了对数据库的操作。
G.添加了文本,图标,和链接的信息,属性为Text,Icon,URL。
数据库属性图(
数据库内容图(
A.通过点击节点获得该节点的编码信息。我们虽然可以很容易的到我们点击的节点事件,只需要implements TreeSelectionListener就可以很容易得到我们的节点触发事件,但是我们只是得到一个节点的实例,而并不是得到其应该有的编码方式,并且这个编码必须是在程序里面利用类的方法实现的,而不是在数据库里面实现,由于树节点和树结构相关的类很多,通过反复的比较和尝试最后选定用递归编码的方式实现,但是这里还是列举了我在思考中想到的办法。
解决办法:
方法一:逐步搜索法:从根节点开始一个一个搜索并且编码,和得到的树节点的事例进行比较,这样我们可以得到这个节点的编码,但是效率实在是很低。而且没有很好的利用各个类的方法。
方法二:递归编码法:得到这个实例后查看这个节点的父节点是否为空,如果为空说明是根节点,如果不为空,利用getIndex得到这个节点以其父节点为根的树的节点中的索引,把这个索引添加到编码字符串的前面,再以其父节点作为子节点继续递归编码,当搜索到根节点的时候直接添加0到最前端这样就实现了编码,具体编码程序如下:
//方法说明: 找到编码的结果,利用递归寻找
//参数说明: DefaultMutableTreeNode tempnode 为该节点的实例
// String save保存编码的结果的字符串
//返回值说明:返回这个节点的编号
public String codeing(DefaultMutableTreeNode tempnode,String save)
{
if (tempnode.getParent() != null) //如果不为空的话,说明有父节点
{
DefaultMutableTreeNode nodeparent = (DefaultMutableTreeNode)tempnode.getParent(); //得到parent信息
int i = nodeparent.getIndex((TreeNode)tempnode); //得到在其父节点下的INDEX号
i=i+1; //
save ="."+i+ save;
return codeing(nodeparent,save);
}
else
{
return "0"+save; //说明是树的根节点
}
}
B.子节点搜索问题,我们要多每个父节点的子节点进行搜索,这样我们每次都需要改变搜索的语句,这样得到的才是我们需要的子节点,我想过用LIKE这种SQL语句但是,由于可能有误差,例如:以0为前缀,我们需要得到和0.i相同的节点,如果只用LIKE 0.i%来表示的话,
//方法功能: 得到一个子女节点
//参数说明: ID为这个父节点的ID号
// index为这个子节点在以这个父节点为根的树中的索引号
//返回值说明:String返回值为这个节点的ID号
public String getonechildID(String ID,long index)
{
String temp = new String(""); //临时变量
try
{
rs = sql.executeQuery("SELECT * FROM NodeInfo WHERE NodeID = '"+
while(rs.next())
{
temp = rs.getString("NodeID");//得到该节点的信息
}
}
catch(SQLException e3)
{
System.out.println("Exception6");
}
return temp; //返回这个子节点的信息
}
C.关于Date类的使用,因为要测试产生节点的时间,比较各种方法的效率问题,所以在我的程序中设计了这个类来获取产生节点的时间,但是由于Date类,在java.util和java.sql两个包中都有,而且这个程序是和数据库相连的程序,所以这两个包必须都引入,但是在定义的时候会出错,通过上网查找我们必须在定义时这样定义:java.sql.Date then,指明用这个包的类来定义一个实例。
D.其他小问题,对于异常的检测办法还是不是很了解,其他的基本都没有问题了。
3.1对相关网站树型菜单的事例研究:
网站地址:http://www.smth.edu.cn/frames.php
网站树型菜单结构图:
网站树型菜实现途径分析:通过对这个BBS的树型菜单的代码和运行情况分析,我们可以从代码中发现对于部分分支节点(数量比较小)采用的是一次性通过HTML文件加载方式实现的,可以说是静态载入的过程,但是对于数量比较大的分支节点是采用数据库异步加载的方式实现的,从这可以看出菜单的具体实现不一定总是限制于一种方法,可以是两种方法结合的特点。
4.实现方法效率比较:
4.1测试环境:
A.操作系统:winXP Professional (sp2)
B.CPU:Intel (R) Pentium(R) processor 1500HMz 主频 598HMz
C.内存:
D.显卡:ATI MOBILITY RADEON 7500
E.游览器: IE 6.0
F.测试项目:静态实现10000个菜单的时间
4.2javascript静态实现(对应于文件夹代码四),for循环创建树节点。
A.重用性:不利于重用
B.运行时间:2243毫秒
C.代码来源:CSDN网站网友
D.分析:相比较其他javascript静态生成方法运行效率很高。
4.3applet静态实现(对应于文件夹代码五),for循环创建树节点。
A.重用性:作为单独利用APPLET技术嵌入网页,可以重用。
B.运行时间:130毫秒
C.代码来源:自己编写
D.分析:运行效率很高。
4.4对上面两中结果的分析:由于javascript是用逐步运行解释的原理进行编译的,所以效率比较底下,而applet实现的是用游览器载入字节码文件,只需要运行就可以,不需要再在客户端编译,而只需要用客户端的虚拟机运行就可以了,所以效率相对于javascript而言效率就比较高,即使是静态的插入节点也很方便应用于网页树型菜单的实现,但是由于javascript是和HTML在一起实现这个功能对于具体的操作系统我们很容易实现对节点连接信息的网页跳转,但是对于applet我们需要运用底层操作实现具体某个游览器的跳转,这也是为什么applet实现树菜单运用这么少的真正原因,因为我们的客户端操作系统大多是微软的WINDOWS系列。
4.5测试说明:我测试的都是静态添加节点的操作所需要的时间,没有进行对数据库载入节点的程序进行测试,可以通过编写程序在数据库中插入10000个节点再进行测试,但是由于时间关系,没有进行编写,但在以后的一段时间会完成这个测试,测试的程序是本文两个和数据库连接的实例。
5.参考文献:
[1] 耿祥义.java大学实用教程 北京:电子工业出版社,2005
[2] 胡恒.javascript网页开发实例教程 北京: 机械工业出版社,2003
[3] 谢若阳.数据结构 北京:清华大学出版社,2005
[4] 周亚辉.数据库系统项目开发实践 北京: 科学出版社 2005
[5] 耿祥义.jsp基础教程 北京:清华大学出版社,2005
[6] 陈志泊 李冬梅 王春玲.数据库原理及应用教程 北京:人民邮电出版社
[7] Kathy Walrath 等.JFC SWING标准教程 北京:电子工业出版社
- 网络中树型菜单实现方法及其效率研究
- 网络中树型菜单实现方法及其效率研究
- 网络中树型菜单实现方法及其效率研究 (代码3)
- 网络中树型菜单实现方法及其效率研究
- (手写识别) Zinnia库及其实现方法研究
- 网络流算法--Ford-Fulkerson方法及其多种实现
- 7. 网络流算法--Ford-Fulkerson方法及其多种实现
- 网络流算法--Ford-Fulkerson方法及其多种实现
- 网络流算法--FORD-FULKERSON方法及其多种实现
- Java List遍历方法 及其效率对比
- Java List遍历方法 及其效率对比
- Java List遍历方法 及其效率对比
- Java List遍历方法 及其效率对比
- 数据仓库多维数据模型研究及其设计方法
- 人工智能及其实现方法
- API实现添加系统托盘图标及其菜单
- FileObserver 研究及其递归监听初步实现
- Java____Timer实现定时功能及其源码研究
- More Effective C++之1
- 调用未知DLL中的导出函数
- Eclipse CVS Team文件传不上去的解决办法.
- 时间日历
- WebLogic WorkShop 8.1 服务器的优化配置
- 网络中树型菜单实现方法及其效率研究
- 对论坛中有关数据类型转换的整理
- CICS FOR NT 安装(一)
- Eclipse3.1 Web开发配置
- ProC动态SQL示例(第1,2,3种方法)
- CICS FOR NT 安装(二)
- gcc介绍
- 开发工具
- C#下WinForm编程:登录窗体的设计