Jive源码分析:tree树形数据结构
Jive是著名的open source论坛,这次我们来研究其帖子结构,论坛是由一个个帖子组成,一个帖子后跟一个或几个回帖,这是一个典型的树形结构,有枝有叶,如下:

1
|-- 3
|-- |--4
|-- |--6
|-- 5

树形结构的应用是非常广泛的,如目录分类系统,菜单系统等等,所以,理解了Jive的应用原理,我们就可以在我们自己的系统中灵活对付树形结构.

这里主要是谈使用关系数据库如Mysql存放树形结构数据,然后我们再在内存中将其展开;以后我们尝试使用XML来对树形结构数据的存储.

Jive中有三个基本对象:
1.Forum :论坛Forum 数据库中对应有forumID;
2.message:帖子,数据库中对应有messageID;
3.Thread:Thread是代表一系列Message的层次结构.可以理解成是messages的目录,是"枝";那么帖子messages当然就是"叶";"枝"和"叶"的区别就是:"枝"下面还可以有"枝"或"叶";而"叶"就是单独元素了.典型的树形数据结构出来了.

在外部,Thread和Messages一样,是作为一个独立的Object存在,可以象创建查询帖子一样创建查询它,因此数据库中对应有ThreadID。

在Thread内部,有两种办法访问它里面的messages:

1.TreeWalker 提供了一种树形结构的视图,这是我们下面要详细讨论的;
2.Iterator 这是一个扁平形的视图。

我们知道,关系数据库是不擅长存放树形结构数据的,因此,一旦使用关系数据库存放树形结构数据,以后访问查询时,免不了有N多SQL查询语句,降低了系统速度;一般最原始的解决办法是在代号上做文章,代号上分几个部分,每个部分用几位数,这种办法局限性显然很大,(可惜写这篇文章时发现就有 易趣 等著名网站使用这种办法。)

而Jive中是使用TreeWalker是将数据库中的数据在内存中建立起树形结构,同时又实现了缓冲,从而大大加快了数据的访问速度。

这里关键是它的一个util类:LongTree 就是这个类提供了对数据的树形结构建立 修改访问等功能:

public final class LongTree implements Cacheable {
//使用这三个数组存放数据 已经数据关系
//我们把要我们自己的数据ID放入数组keys中,leftChildren和rightSiblings是记录数组keys的数组ID之间关系
//leftChildren放置的是儿子 rightSiblings存放的是其兄弟(并列的〕    
long [] keys;
//char arrays let us address get about 65K nodes.
char [] leftChildren;
char [] rightSiblings;

// Pointer to next available slot.
char nextIndex = 2;

/**
* 构造一个新的Tree只要提供两个参数:根数据ID 和 Tree结构的大小
*/
public LongTree(long rootKey, int size) {
  keys = new long[size+1];
  leftChildren = new char[size+1];
  rightSiblings = new char[size+1];

  //新Tree,第一个元素当然存放我们提供的参数:根数据ID :rootKey
  // New tree, so set the fields to null at root.
  keys[1] = rootKey;
  leftChildren[1] = 0;
  rightSiblings[1] = 0;
}

/**
* Adds a child to the tree.
*
* @param parentKey 将新的儿子加入哪个父亲下面,所以需要提供父亲的数据ID
* @param newKey 新的儿子本身的数据ID
*/
public void addChild(long parentKey, long newKey) {
  // Find record for parent 检查提供的父亲是否存在
  char parentIndex = findKey(parentKey, (char)1);
  if (parentIndex == 0) {
    throw new IllegalArgumentException("Parent key " + parentKey +
    " not found when adding child " + newKey + ".");
  }

  // Create record for new key.加入数组 前后关系暂时设定为0
  keys[nextIndex] = newKey;
  leftChildren[nextIndex] = 0;
  rightSiblings[nextIndex] = 0;

  // Adjust references. Check to see if the parent has any children.
  if (leftChildren[parentIndex] == 0) {
    // No children, therefore make the new key the first child.
    leftChildren[parentIndex] = nextIndex;
  }
  else {
    // The parent has children, so find the right-most child.
    //如果这个父亲有儿子了,那么找出其最右边的儿子,类似上图的 6
    //这样是为了将新的儿子追加在其后面,或者说最最右边
    long siblingIndex = leftChildren[parentIndex];
    while (rightSiblings[new Long(siblingIndex).intValue()] != 0) {
      siblingIndex = rightSiblings[new Long(siblingIndex).intValue()];
    }
    // Add the new entry as a sibling of that last child.
    rightSiblings[new Long(siblingIndex).intValue()] = nextIndex;
    }

    // Finally, increment nextIndex so it's ready for next add.
    nextIndex++;
}

...........

}

从上面简单的分析LongTree的加入儿子的方法,我们大概理解这个类的原理和使用方法,让我们来看看TreeWalker是怎么使用LongTree的。

TreeWalker有个子类DbTreeWalker:

我们先看看存放树形结构数据的数据库:

CREATE TABLE jiveMessage (
  messageID BIGINT NOT NULL,
  parentMessageID BIGINT NULL, #记录当前帖子的父亲
  threadID BIGINT NOT NULL, #当前帖子的threadID 就是目录ID
  forumID BIGINT NOT NULL,
  userID BIGINT NULL,
  subject VARCHAR(255),
  body TEXT,

....

)

我们看到其数据库主要用两个字段来指明这种树形结构数据的之间的关系。

再看看DbTreeWalker是怎么使用LongTree把存放在这个数据库中数据展现开来的。

public class DbTreeWalker implements TreeWalker {
/** DATABASE QUERIES SQL 语句**/
private static final String GET_MESSAGES =
"SELECT messageID, parentMessageID, creationDate FROM jiveMessage " +
"WHERE threadID=? AND parentMessageID IS NOT NULL " +
"ORDER BY creationDate ASC";

........

private long threadID;
private DbForumFactory factory;
//这里设置使用LongTree准备生成一个对象
private LongTree tree;

/**
* TreeWalker 构造函数
*/
public DbTreeWalker(DbForumThread thread, DbForumFactory factory) {
  this.threadID = thread.getID();
  this.factory = factory;

  ForumMessage root = thread.getRootMessage();
  int numMessages = thread.getMessageCount(IGNORE_MODERATION_FILTER);

  // Create the tree, set the root.创建一个新的Tree
  tree = new LongTree(root.getID(), numMessages);

  // Now, build the rest of the tree.建立完整这个树
  //到数据库中,将当前目录下 (当前Thread)所有数据装入这个Tree
  Connection con = null;
  PreparedStatement pstmt = null;
  try {
    con = ConnectionManager.getConnection();
    pstmt = con.prepareStatement(GET_MESSAGES);
    pstmt.setLong(1, thread.getID());
    ResultSet rs = pstmt.executeQuery();
    while(rs.next()) {
      long messageID = rs.getLong(1);
      long parentMessageID = rs.getLong(2);
      //加入儿子
      tree.addChild(parentMessageID, messageID);
    }
  }

  。。。。。
  }
}

.......

//可以很方便的得到一个帖子的父亲
public ForumMessage getParent(ForumMessage message)
throws ForumMessageNotFoundException
{
........
}

//可以很方便的得到一个帖子的儿子
public ForumMessage getChild(ForumMessage message, int index)
throws ForumMessageNotFoundException
{
...........
}

Jive中使用Cache技术,保证了经常访问的那些Tree能够放置在内存中,这是另外篇幅需要研究的。

总之,我们找到了一套树形结构数据+关系数据库+面向对象语言三者结合的一种办法,这种办法无论从理论上 以及实用性上,都是值得我们在实践中借鉴的。

出处:www.jdon.com

Jive源码分析:tree树形数据结构相关推荐

  1. mysql源码分析——索引的数据结构

    引子 说几句题外话,在京被困三个月之久,不能回家,所以这个源码分析就中断了.之所以在家搞这个数据库的源码分析,主要是在家环境齐全,公司的电脑老旧不堪.意外事件往往打断正常的习惯和运行轨迹,但这却是正常 ...

  2. Nginx源码分析:核心数据结构ngx_cycle_t与内存池概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 核心数据结构与内存池概述 在Nginx中的核心数据结构就是ngx_cycle_t结构,在初始 ...

  3. JNI实现源码分析【二 数据结构】

    正文 在展开深入讨论之前,先说一下Dalvik中和JNI相关的数据结构,是很有必要的. 在Object.h中定义了很多的数据结构: 0x01: 虚拟机中的对象 我们知道,Java是面向对象的,Java ...

  4. [flite源码分析一]常用数据结构cst_val

    flite里的基础数据结构,最常见的是cst_val.这个结构设计的很巧妙. 有如下特点 最基础的数据结构,可以用于存储int,float,string,和其他对象.同样可以存放列表和树. cst_v ...

  5. libev源码分析(一)libev数据结构整理

    这里选取的版本为最新版:libev-4.04.libev的代码很简练,除了对高效I/O模型等的封装文件,核心文件就两个:ev.h和ev.c,其中ev.c大概4000行左右.代码大量用到了宏,并且宏还嵌 ...

  6. 鸿蒙轻内核M核源码分析:数据结构之任务就绪队列

    摘要:本文会给读者介绍鸿蒙轻内核M核源码中重要的数据结构,任务基于优先级的就绪队列Priority Queue. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列三 数据结构-任务就绪队列> ...

  7. 集合底层源码分析之HashMap《上》(三)

    集合底层源码分析之HashMap<上>(三) 前言 源码分析 HashMap主要属性及构造方法分析 tableSizeFor()方法源码分析 Node类源码分析 TreeNode类源码分析 ...

  8. JNI实现源码分析【三 间接引用表】

    在JNI实现源码分析[二 数据结构]的参数传递一节中,我们提到,JNI为了安全性的考虑使用了形如jobject的结构来传递参数.而jobject被表述为指针,但又不是直接指向Object的指针那么jo ...

  9. pppoe协议和pppd源码分析

    原文链接:http://blog.chinaunix.net/uid-26675482-id-4133784.html pppoe协议简介 (一)发现(Discovery)阶段 在发现(Discove ...

  10. 【esp32-adf】按键服务源码分析

    一.adc按键 二.源码分析 2.1 数据结构 设备集合 typedef struct esp_periph_sets {EventGroupHandle_t state_event_bits;xSe ...

最新文章

  1. .svn文件夹_新建SVN仓库并上传项目
  2. 高通linux内核目录,高通 android 源代码以及目标系统目录结构
  3. 内核中修改和保存defconfig的方法
  4. 博图如何读取mysql数据_博途使用小结:从SQL中读取数据并给变量赋值
  5. consul历史版本下载地址
  6. cassandra_Spring Boot Cassandra的第一步
  7. php 数据类型转换与比较
  8. sql 账号查询一个表查询权限_一个查询语句引发的问题以及巨型表相关操作探索与思考...
  9. c语言词典课程设计报告,C语言课程设计英语词典排版系统
  10. 【转载】通往性能优化的天堂-地狱 JOIN方法说明
  11. 查看loadrunner代码行号
  12. 一个较好的基础的数据库连接池知识
  13. 搭建webservice服务报错求解
  14. informatic对表的增量抽取机制
  15. 计算机程序运行异常处理,Win7电脑运行程序出现appcrash错误怎么解决?
  16. C语言数据结构与算法-------查找(二)哈希法
  17. html隐藏不占位置,css中隐藏不占空间怎么设置?
  18. Java调用阿里云OSS下载文件
  19. linux删除的快捷键
  20. 从raspberry pi OS 64-bit lite开始安装图形界面(icewm ,dwm)

热门文章

  1. [0418] 程序设计实训小结(更新1420)
  2. 蓝桥杯进制转换的一题……
  3. 15.SpringMVC和Spring上下文关系(为什么SpringMVC可以调用到Spring)
  4. 【学习笔记】【C语言】循环结构-do while
  5. 使用Oracle数据库进行企业开发(三)
  6. 『C#基础作业』4.类的静态成员示例
  7. idea中 Application Server not specified
  8. MDI窗体容器和权限设置.avi
  9. Mybatis常见配置错误总结
  10. 2013年阿里巴巴实习生笔试题