(原创)统计分析系统中的树形结构统计报表--利用加权多叉树实现
一、由TreeGrid树形结构表格想到的
在阅读本文之前,请先依次阅读我的前两篇文章:分别是《多叉树结合JavaScript树形控件实现无限级树
形菜单(一种构建多级有序树形结构JSON(或XML)数据源的方法)》http://www.iteye.com/topic/1122125
和《(原创)新概念智能树形菜单--利用加权多叉树结合JavaScript树形控件实现》http://www.iteye.com/topic/1122737,这两篇文章是本文的基础,本文就是在前两篇文章的基础上联想到的,“多叉树”的概念是在第一篇文章里面提出来的,“加权多叉树”的概念是在第二篇文
章里提出来的,这三篇文章的思路具有连贯性,顺序不能颠倒。
让我们先回顾一下我的第一篇文章中提到的关于TreeGrid树形表格的部分:
利用多叉树可以实现树形表格的三个主要功能,分别是:
1、一次性构造树形表格,实现数据分级展示
2、通过更换比较器,实现对不同表格列的全排序(全排序指的是对所有页的数据进行排序,而不是
只对当前页的数据排序)
3、实现对树形表格的完整分页(每次分页时,只取固定数目的第一层节点,之后调用toString方法
,展示出完整条数的分级数据)
如图所示:
在统计分析系统中,可以利用这种树形表格实现维度钻取功能,逐级钻取查看更细粒度的指标数据,那么
如何根据维度层次和最细粒度的指标数据,汇总生成这样一个树形结构指标统计报表呢?
我在第二篇文章中论述了如何利用加权多叉树实现智能树形菜单,那么这张树形报表能否再智能一些呢?
能否利用加权多叉树挖掘出更深层次的用户信息呢?
带着这两个问题,让我们从分析这棵多叉树开始。。。。。。
假设某统计分析系统对各个地区的居民家庭收入情况进行统计分析,前端报表展现模块首先需要将数据库
中的客户级(这里的“客户”指的是一个一个的家庭,以家庭为单位)细粒度指标数据按照行政地区的维
度层次进行逐级汇总后,在内存中构造一棵加权多叉树,每个节点的默认权值都为1,如图所示:
这棵树构造好之后,就可以通过先序遍历递归输出树形报表了。
假设需要做一张各个地区的居民家庭收入情况树形统计报表,通过逐级汇总和数据钻取,我们可以查看各
个地区的居民家庭收入情况,最高层可以汇总到省级,最细粒度可以分解到家庭,这只是一个家庭收入统
计上的简单分析,能否在此基础上进一步挖掘出一些其他的东西呢?由于这颗树的叶子节点代表一个一个
的家庭,我们能否从叶子节点中分析出一些东西呢?
受智能树形菜单(见第二篇文章)的启发,在原有的树形报表中可以再提供两个功能:加权排序和客户筛
选重新汇总。
现在先对两个概念做一下定义:
1、加权排序
指的是对分支路径(分支路径指的是从根节点到叶子节点的一条完整路径,例如“东北地区->黑龙江->哈
尔滨->道外区->王小利”)增加权值,然后对整棵树进行兄弟节点横向排序,展示出排序之后的树形报表
。
2、客户筛选,重新汇总
指的是从叶子节点中搜索到满足某一条件的叶子,然后从叶子节点开始,重新向上汇总指标数据,汇总数
据时可以采用倒遍历的方法,所谓倒遍历,指的是从叶子节点开始,向上遍历节点,直到根节点,在遍历
的过程中对节点数据进行汇总,在进行倒遍历之前,需要对除叶子之外的节点数据清零,然后对所有搜索
到的叶子节点进行倒遍历汇总。
这里提到的“倒遍历汇总”,正是解决前面第一个问题的方法:根据维度层次和最细粒度的指标数据,汇
总生成树形结构。
现在对这两个概念分别举例,
先说加权排序吧:
假设有这样一个需求,政府的分析人员不想按照各个行政地区下的家庭平均月收入的高低对报表进行排序
,而是要换一种排序的方式,以便进一步挖掘出各地区家庭平均月收入背后隐藏的信息,比如在某一个区
县的家庭平均月收入中,哪些家庭的月收入比较低,比如某区县的家庭平均月收入是7000元,其中月收入
1000元以下的家庭有800个,其余都是月收入大于1000元的家庭,能不能在树形报表的展示上体现这些信
息。比如将贫困家庭数量多的区县排在前面,贫困家庭数量少的区县排在后面,所谓贫困家庭,指的是家
庭平均月收入小于一定额度的家庭,例如平均月收入小于1000元的家庭为贫困家庭,政府可以分析出哪些
地区的贫困家庭数量最多,哪些地区的贫困家庭数量最少,对贫困家庭数量最多的地区重点关注等等,甚
至可以钻取到客户级,钻取到叶子节点,看看具体哪些家庭是贫困家庭,详细了解他们的家庭成员信息及
收入情况,然后对这些贫困家庭进行重点扶持,给予他们重点关注和优惠策略,做到“以人为本”。当然
,在一张报表中直接钻取到客户级,数据量太大了,在展现上也不太现实,可以变通一下,只在树形结构
报表中展示客户级以上的汇总数据,最多钻取到客户级的上一级(例如钻取到区县级),当点击区县级的
节点时,在界面中弹出一个子窗口,在这个子窗口中列出客户信息的数据列表。
要想实现这样的需求,需要对树中所有的叶子节点进行遍历,然后对贫困家庭的叶子节点所在的分支路径
权值加1,然后对树进行兄弟节点横向排序。
这是加权排序之后的树,如图所示:
再说客户筛选,重新汇总:
假设分析人员想在加权排序的基础上,过滤掉那些普通的家庭,只保留贫困家庭,只对贫困家庭的平均月
收入进行统计。
要实现这种需求,需要对树中所有的叶子节点进行遍历,然后将贫困家庭的叶子节点所在的分支路径设置
为可见,其它路径设置为不可见,然后对这些筛选出来的叶子节点进行倒遍历汇总,展示出筛选之后的树
形报表,如图所示:
好了,加了这两个新的概念之后,我们再梳理一下统计分析系统树形结构报表的功能:
1)一次性构造多级数据列表,展现树形结构报表,实现数据钻取功能;
2)可以按不同指标对树形结构报表进行全排序(例如按家庭平均月收入排序);
3)实现对树形结构报表的完整分页(每次分页时,只取固定数目的第一层节点,展示出完整条数的分级
数据,也就是说树形报表分页时,每页的总数据条数是不固定的,必须是一棵完整的树形结构);
4)可以对树形结构报表进行加权排序,可以对任意级别的节点进行向上加权(所谓向上加权,指的是对
该节点的所有上级节点(包括该节点在内)进行加权,被向上加权的节点必须处于同一层级(例如对区县
级节点进行向上加权,加权的条件是家庭平均月收入小于1000元的区县),这样加权排序才有意义);
5)可以对树形结构报表进行筛选并重新汇总,可以对任意级别的节点进行筛选(每次筛选必须筛选同一
层级的节点(例如对区县级节点进行筛选,筛选的条件是家庭平均月收入小于1000元的区县),这样筛选
才有意义),筛选出来之后,将该节点的所有上级节点设置为可见,将该节点的所有下级节点设置为可见
,其它节点设置为不可见。
现在需要我们把上面提到的两个概念修正一下,把“加权排序”修改为“同级节点向上加权排序”,把“
客户筛选,重新汇总”修改为“同级节点筛选,向上重新汇总”,这样就恰当了。
现在让我们把注意力集中在“同级节点筛选,向上重新汇总”上,那么如何筛选同级节点呢?在我的第二
篇文章中,筛选功能叶子时,采用的方法是:构造一个功能叶子列表,本质上是一个叶子节点对象的引用
的数组,在C语言里面就是指针数组,然后循环遍历这个对象引用数组,根据判断条件找到满足条件的叶
子节点。如果只是搜索叶子的话,建立一个叶子引用的列表就可以了,但是我们这里可能对多个层级的节
点进行筛选或者向上加权,按照那种方法的话,有多少层就得建立多少个节点列表,每一层都得建立一个
节点列表,筛选的时候,在某一层的节点列表中进行筛选,这样就太麻烦了。既然是线性查找,那么可以
用节点链表的形式取代这个节点引用数组,也就是说在每个节点中增加两个引用,前一个引用指向同一层
级的前一个节点,后一个引用指向同一层级的下一个节点,同一层级的第一个节点和最后一个节点也要建
立这种互相引用的关系,也就是说对同一层级的节点连成一个双向循环链表(其实用单向循环链表就可以
,构造双向循环链表也许以后会有用,之所以构造循环链表,是因为横向排序之后,同一层级节点顺序会
被打乱,而每次筛选都是从第一个节点开始,所以为了保证遍历到该层级所有节点,需要使用循环链表)
,从第一个节点开始,逐个向后遍历节点,逐个筛选。此时一个节点中会包含四种指针,分别是:孩子指
针列表、父节点指针、前节点指针、后节点指针,如下图所示:
如果对某一层节点(例如对区县级节点)进行向上加权的话,首先需要从根节点开始沿着最左侧路径,找
到那一层的第一个节点,然后从这个节点开始沿着链表的指向遍历本层节点,遇到满足条件的节点,就向
上加权,如下图所示:
蓝色箭头代表节点的遍历的顺序;黄色箭头代表向上加权的顺序,红色线条代表被加权的路径,图中“吉
林->长春->二道区“被加权。
如果对某一层节点(例如对区县级节点)进行筛选的话,首先需要从根节点开始沿着最左侧路径,找到那
一层的第一个节点,然后从这个节点开始沿着链表的指向遍历本层节点,遇到满足条件的节点,就向上重
新汇总,然后将该节点所在的路径设置为可见,其它路径设置为不可见,如下图所示:
蓝色箭头代表节点的遍历的顺序;红色箭头代表向上重新汇总的顺序,绿色线条代表可见的路径,图中“
二道区“和”铁东区“为被筛选的节点。
由于这棵树实现了上级节点与下级节点互相引用,同级节点与同级节点互相引用,竖着看是一棵多叉树,
横着看,每一层都是一个双向循环链表,所以可以形象的称这棵树叫做“手拉手”树形结构,如图所示:
我们也给这棵树起个名字,叫做hand-in-hand tree,简称H-Tree。
下面用JAVA来演示统计分析系统中的树形结构报表中所描述的五个功能。
比如要做一张类似于如下这样的各个地区的家庭平均月收入统计报表,如图所示:
二、源代码演示(演示4个功能,分页除外)
package test;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collections;
/**
* 树形报表类,
* 东北地区家庭平均月收入统计报表
*/
public class ReportTree {
public static void main(String[] args) {
// 读取层次数据结果集列表
List dataList = VirtualDataGenerator.getVirtualResult();
// 构造加权多叉树(纵向拉手)
Node root = buildWeightedMultiTree(dataList);
// 对树的每一层级节点构造双向循环链表(横向拉手)
buildCircularlyDoublyLinkedList(root);
// 从客户级开始,逐级向上汇总指标数据(这里汇总家庭平均月收入)
// 汇总时并不是简单的累加,而是计算家庭平均月收入的平均值
calculateMeasure(root, 5);
// 指标排序,按各个地区的家庭平均月收入进行排序
sortMeasure(root);
// 输出按家庭平均月收入升序排序后的报表
System.out.println("按家庭平均月收入升序排序后的报表:\n" + root.toString());
// 对家庭平均月收入小于1000的家庭进行向上加权,并按权值排序,也就是说将贫困
家庭数量多的地区排在前面
upIncreaseRouteWeightAndSort(root, 5);
// 输出向上加权并排序后的报表
System.out.println("按各个地区的贫困家庭(家庭平均月收入小于1000的家庭)数量
的多少排序:\n" + root.toString());
// 对家庭平均月收入小于1000的家庭进行筛选并重新汇总,并按家庭平均月收入排序
,只列出贫困家庭
filterAndCalculateMeasure(root, 5);
// 输出筛选并排序后的报表
System.out.println("只统计家庭平均月收入小于1000的家庭:\n" + root.toString
());
// 树形报表的输出结果,假设这是一个简单的东北地区家庭平均月收入统计报表,每
一行由 行政地区 + 家庭平均月收入 组成,如下所示:
// 按家庭平均月收入升序排序后的报表:
// |--东北地区 9077.25
// |--黑龙江 3461.67
// |--哈尔滨 1792.5
// |--南岗区 885.0
// |--李盛伟家 880.0
// |--李磊家 890.0
// |--道外区 2700.0
// |--王泉历家 900.0
// |--吴艳清家 4500.0
// |--齐齐哈尔 6800.0
// |--龙沙区 6800.0
// |--李龙波家 6000.0
// |--李涛家 7600.0
// |--吉林 10875.0
// |--长春 10875.0
// |--二道区 6750.0
// |--李雪涛家 3500.0
// |--李润杰家 10000.0
// |--宽城区 15000.0
// |--窦晓峰家 10000.0
// |--李坤奇家 20000.0
// |--辽宁 13494.33
// |--鞍山 13091.5
// |--铁东区 4683.0
// |--金城家 666.0
// |--王琪跃家 8700.0
// |--铁西区 21500.0
// |--朱一家 13000.0
// |--由建宏家 30000.0
// |--辽阳 14300.0
// |--东京城 14300.0
// |--李建保家 8600.0
// |--龙明超家 20000.0
//
// 按各个地区的贫困家庭(家庭平均月收入小于1000的家庭)数量的多少排序:
// |--东北地区 9077.25
// |--黑龙江 3461.67
// |--哈尔滨 1792.5
// |--南岗区 885.0
// |--李盛伟家 880.0
// |--李磊家 890.0
// |--道外区 2700.0
// |--王泉历家 900.0
// |--吴艳清家 4500.0
// |--齐齐哈尔 6800.0
// |--龙沙区 6800.0
// |--李龙波家 6000.0
// |--李涛家 7600.0
// |--辽宁 13494.33
// |--鞍山 13091.5
// |--铁东区 4683.0
// |--金城家 666.0
// |--王琪跃家 8700.0
// |--铁西区 21500.0
// |--朱一家 13000.0
// |--由建宏家 30000.0
// |--辽阳 14300.0
// |--东京城 14300.0
// |--李建保家 8600.0
// |--龙明超家 20000.0
// |--吉林 10875.0
// |--长春 10875.0
// |--二道区 6750.0
// |--李雪涛家 3500.0
// |--李润杰家 10000.0
// |--宽城区 15000.0
// |--窦晓峰家 10000.0
// |--李坤奇家 20000.0
//
// 只统计家庭平均月收入小于1000的家庭:
// |--东北地区 834.0
// |--辽宁 666.0
// |--鞍山 666.0
// |--铁东区 666.0
// |--金城家 666.0
// |--黑龙江 890.0
// |--哈尔滨 890.0
// |--南岗区 885.0
// |--李盛伟家 880.0
// |--李磊家 890.0
// |--道外区 900.0
// |--王泉历家 900.0
}
/**
* 构造加权多叉树
* @return
*/
public static Node buildWeightedMultiTree(List dataList) {
// 节点列表(散列表,用于临时存储节点对象)
HashMap nodeList = new HashMap();
// 根节点
Node root = null;
// 根据结果集构造节点列表(存入散列表)
for (Iterator it = dataList.iterator(); it.hasNext();) {
Map dataRecord = (Map) it.next();
Node node = new Node();
node.id = (String) dataRecord.get("id");
node.text = (String) dataRecord.get("text");
node.parentId = (String) dataRecord.get("parentId");
node.money = Double.parseDouble((String) dataRecord.get("money"));
nodeList.put(node.id, node);
}
// 构造无序的多叉树
Set entrySet = nodeList.entrySet();
for (Iterator it = entrySet.iterator(); it.hasNext();) {
Node node = (Node) ((Map.Entry) it.next()).getValue();
if (node.parentId == null || node.parentId.equals("")) {
root = node;
} else {
((Node) nodeList.get(node.parentId)).addChild(node);
// 在节点中增加一个父节点的引用
node.parentNode = (Node) nodeList.get(node.parentId);
}
}
return root;
}
/**
* 对树的每一层级节点构造双向循环链表,将处于同一层级的不同父节点下的子节点首尾相连
*/
public static void buildCircularlyDoublyLinkedList(Node root) {
// 先将树中的每个父节点下的兄弟节点连成双向链表
root.buildDoublyLinkedList();
// 再将处于同一层级的不同父节点下的子节点首尾相连
for (Node firstNode = root.getFirstChild(); firstNode != null; firstNode =
firstNode.getFirstChild()) {
Node currentParent = firstNode.parentNode;
Node nextParent = currentParent.nextNode;
while (nextParent != null && nextParent != firstNode.parentNode) {
currentParent.getLastChild().nextNode =
nextParent.getFirstChild();
nextParent.getFirstChild().previousNode =
currentParent.getLastChild();
currentParent = nextParent;
nextParent = nextParent.nextNode;
}
firstNode.previousNode = currentParent.getLastChild();
currentParent.getLastChild().nextNode = firstNode;
}
}
/**
* 从客户级开始,逐级向上汇总指标数据(这里汇总家庭平均月收入)
* 汇总时并不是简单的累加,而是计算家庭平均月收入的平均值
* @param root
*/
public static void calculateMeasure(Node root, int level) {
int i = 1;
for (Node firstNode = root.getFirstChild(); firstNode != null; firstNode =
firstNode.getFirstChild()) {
if (++i == level) {
for (Node currentNode = firstNode; currentNode != null;
currentNode = currentNode.nextNode) {
currentNode.calculateMeasure();
if (firstNode == currentNode.nextNode) {
break;
}
}
for (Node currentNode = firstNode; currentNode != null;
currentNode = currentNode.nextNode) {
currentNode.avgMeasure();
if (firstNode == currentNode.nextNode) {
break;
}
}
}
}
}
/**
* 指标排序,按各个地区的家庭平均月收入进行排序
* @return
*/
public static void sortMeasure(Node root) {
root.sortChildrenByMoney();
}
/**
* 对家庭平均月收入小于等于1000的家庭进行向上加权,并按权值排序
* @param root
* @param level
*/
public static void upIncreaseRouteWeightAndSort(Node root, int level) {
int i = 1;
for (Node firstNode = root.getFirstChild(); firstNode != null; firstNode =
firstNode.getFirstChild()) {
if (++i == level) {
for (Node currentNode = firstNode; currentNode != null;
currentNode = currentNode.nextNode) {
if (currentNode.money <= 1000) {
currentNode.upIncreaseRouteWeight();
}
if (firstNode == currentNode.nextNode) {
break;
}
}
}
}
root.sortChildrenByWeight();
}
/**
* 对家庭平均月收入小于等于1000的家庭进行筛选重新汇总,并按家庭平均月收入排序
* @param root
* @param level
*/
public static void filterAndCalculateMeasure(Node root, int level) {
int i = 1;
for (Node firstNode = root.getFirstChild(); firstNode != null; firstNode =
firstNode.getFirstChild()) {
if (++i == level) {
// 将该层级以上的节点数据清零
for (Node currentNode = firstNode; currentNode != null;
currentNode = currentNode.nextNode) {
for (Node parentNode = currentNode.parentNode;
parentNode != null; parentNode = parentNode.parentNode) {
parentNode.money = 0;
parentNode.traversalCount = 0;
parentNode.hasCulAvg = false;
}
if (firstNode == currentNode.nextNode) {
break;
}
}
root.setTreeNotVisible();
// 筛选并重新汇总数据
for (Node currentNode = firstNode; currentNode != null;
currentNode = currentNode.nextNode) {
if (currentNode.money <= 1000) {
currentNode.calculateMeasure();
currentNode.setRouteVisible();
currentNode.setTreeVisible();
}
if (firstNode == currentNode.nextNode) {
break;
}
}
// 计算指标平均值
for (Node currentNode = firstNode; currentNode != null;
currentNode = currentNode.nextNode) {
if (currentNode.money <= 1000) {
currentNode.avgMeasure();
}
if (firstNode == currentNode.nextNode) {
break;
}
}
}
}
root.sortChildrenByMoney();
}
}
/**
* 节点类
*/
class Node {
/**
* 节点编号
*/
public String id;
/**
* 节点内容
*/
public String text;
/**
* 父节点编号
*/
public String parentId;
/**
* 节点权值
*/
public int weight = 1;
/**
* 是否可见,默认为true
*/
public boolean visible = true;
/**
* 父节点引用
*/
public Node parentNode;
/**
* 前一个节点的引用
*/
public Node previousNode;
/**
* 下一个节点的引用
*/
public Node nextNode;
/**
* 家庭平均月收入
*/
public double money;
/**
* 倒遍历时对该节点的访问次数
*/
public int traversalCount;
/**
* 是否已经计算过平均值
*/
public boolean hasCulAvg = false;
/**
* 孩子节点列表
*/
private Children children = new Children();
// 添加孩子节点
public void addChild(Node child) {
children.addChild(child);
}
// 获取第一个孩子节点
public Node getFirstChild() {
return children.getFirstChild();
}
// 获取最后一个孩子节点
public Node getLastChild() {
return children.getLastChild();
}
// 判断是否叶子节点
public boolean isLeaf() {
return children == null || children.getSize() == 0;
}
// 产生报表表格行前面的空格
private static String GeneWhiteSpace(Node node) {
int level = 1;
for (Node parentNode = node.parentNode; parentNode != null; parentNode =
parentNode.parentNode) {
level++;
}
int number = 2 * level;
String space = "";
for (int i = 0; i < number; i++) {
space += " ";
}
space += "|--";
return space;
}
// 先序遍历,拼接报表表格行
public String toString() {
if (visible) {
String result = GeneWhiteSpace(this) + text + " " + money + "\n";
if (children != null && children.getSize() != 0) {
result += children.toString();
}
return result;
} else {
return "";
}
}
// 兄弟节点横向排序(按家庭平均月收入排序)
public void sortChildrenByMoney() {
if (children != null && children.getSize() != 0) {
children.sortChildrenByMoney();
}
}
// 兄弟节点横向排序(按权值排序)
public void sortChildrenByWeight() {
if (children != null && children.getSize() != 0) {
children.sortChildrenByWeight();
}
}
// 先序遍历,设置该节点下的所有功能路径为不可见
public void setTreeNotVisible() {
visible = false;
if (children != null && children.getSize() != 0) {
children.setTreeNotVisible();
}
}
// 先序遍历,设置该节点下的所有功能路径为可见
public void setTreeVisible() {
visible = true;
if (children != null && children.getSize() != 0) {
children.setTreeVisible();
}
}
// 设置包含该节点的功能路径可见(将该节点及其所有上级节点设置为可见)
public void setRouteVisible() {
visible = true;
for (Node parentNode = this.parentNode; parentNode != null; parentNode =
parentNode.parentNode) {
parentNode.visible = true;
}
}
// 对包含该节点的功能路径向上加权
public void upIncreaseRouteWeight() {
weight++;
for (Node parentNode = this.parentNode; parentNode != null; parentNode =
parentNode.parentNode) {
parentNode.weight++;
}
}
// 向上指标汇总,这里先汇总家庭平均月收入总和
// 然后再在main函数中通过avgMeasure()方法计算家庭平均月收入的平均值
public void calculateMeasure() {
for (Node parentNode = this.parentNode; parentNode != null; parentNode =
parentNode.parentNode) {
parentNode.money += money; // 收入累加
parentNode.traversalCount++; // 访问次数累加
}
}
// 对所有兄弟节点构造双向链表
public void buildDoublyLinkedList() {
if (children != null && children.getSize() != 0) {
children.buildDoublyLinkedList();
}
}
// 计算指标平均值(这里计算家庭平均月收入的平均值)
public void avgMeasure() {
for (Node parentNode = this.parentNode; parentNode != null; parentNode =
parentNode.parentNode) {
if (!parentNode.hasCulAvg) {
parentNode.money = (new BigDecimal
(parentNode.money)).divide(
new BigDecimal(parentNode.traversalCount),
2,
BigDecimal.ROUND_HALF_UP).doubleValue();
parentNode.hasCulAvg = true;
}
}
}
}
/**
* 孩子列表类
*/
class Children {
private List list = new ArrayList();
public int getSize() {
return list.size();
}
public Node getFirstChild() {
return getSize() == 0 ? null : (Node) list.get(0);
}
public Node getLastChild() {
return getSize() == 0 ? null : (Node) list.get(list.size() - 1);
}
public void addChild(Node node) {
list.add(node);
}
// 拼接孩子节点的表格行数据
public String toString() {
String result = "";
for (Iterator it = list.iterator(); it.hasNext();) {
Node node = (Node) it.next();
if (node.visible) {
result += node.toString();
}
}
return result;
}
// 孩子节点排序(按家庭平均月收入排序)
public void sortChildrenByMoney() {
// 对本层节点进行排序
// 可根据不同的排序属性,传入不同的比较器,这里传入家庭平均月收入比较器
Collections.sort(list, new NodeMoneyComparator());
// 对每个节点的下一层节点进行排序
for (Iterator it = list.iterator(); it.hasNext();) {
((Node) it.next()).sortChildrenByMoney();
}
}
// 孩子节点排序(按权值排序)
public void sortChildrenByWeight() {
// 对本层节点进行排序
// 可根据不同的排序属性,传入不同的比较器,这里传入优先级比较器
Collections.sort(list, new NodePriorityComparator());
// 对每个节点的下一层节点进行排序
for (Iterator it = list.iterator(); it.hasNext();) {
((Node) it.next()).sortChildrenByWeight();
}
}
// 设置孩子节点为不可见
public void setTreeNotVisible() {
for (Iterator it = list.iterator(); it.hasNext();) {
((Node) it.next()).setTreeNotVisible();
}
}
// 设置孩子节点为可见
public void setTreeVisible() {
for (Iterator it = list.iterator(); it.hasNext();) {
((Node) it.next()).setTreeVisible();
}
}
// 对孩子节点构造双向链表
public void buildDoublyLinkedList() {
for (int i = 0; i < list.size() - 1; i++) {
((Node) list.get(i)).nextNode = (Node) list.get(i+1);
((Node) list.get(i+1)).previousNode = (Node) list.get(i);
}
for (Iterator it = list.iterator(); it.hasNext();) {
((Node) it.next()).buildDoublyLinkedList();
}
}
}
/**
* 家庭平均月收入比较器
*/
class NodeMoneyComparator implements Comparator {
// 按照家庭平均月收入 比较
public int compare(Object o1, Object o2) {
// 按照家庭平均月收入由低到高排序
double i1 = ((Node)o1).money;
double i2 = ((Node)o2).money;
return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1);
}
}
/**
* 节点权值比较器
*/
class NodePriorityComparator implements Comparator {
// 按照 (节点权值+家庭平均月收入) 比较
public int compare(Object o1, Object o2) {
// 按权值由大到小排序
int w1 = ((Node)o1).weight;
int w2 = ((Node)o2).weight;
if (w1 < w2) {
return 1;
} else if (w1 > w2) {
return -1;
} else { // 权值相等时,按照家庭平均月收入由高到低排序
double i1 = ((Node)o1).money;
double i2 = ((Node)o2).money;
return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1);
}
}
}
/**
* 构造虚拟的层次数据
*/
class VirtualDataGenerator {
// 构造无序的结果集列表,实际应用中,该数据应该从数据库中查询获得;
// 根据维度层次和细粒度指标数据查询获得
public static List getVirtualResult() {
List dataList = new ArrayList();
HashMap dataRecord1 = new HashMap();
dataRecord1.put("id", "0");
dataRecord1.put("text", "东北地区");
dataRecord1.put("parentId", "");
dataRecord1.put("money", "0");
HashMap dataRecord2 = new HashMap();
dataRecord2.put("id", "1");
dataRecord2.put("text", "黑龙江");
dataRecord2.put("parentId", "0");
dataRecord2.put("money", "0");
HashMap dataRecord3 = new HashMap();
dataRecord3.put("id", "2");
dataRecord3.put("text", "吉林");
dataRecord3.put("parentId", "0");
dataRecord3.put("money", "0");
HashMap dataRecord4 = new HashMap();
dataRecord4.put("id", "3");
dataRecord4.put("text", "辽宁");
dataRecord4.put("parentId", "0");
dataRecord4.put("money", "0");
HashMap dataRecord5 = new HashMap();
dataRecord5.put("id", "11");
dataRecord5.put("text", "哈尔滨");
dataRecord5.put("parentId", "1");
dataRecord5.put("money", "0");
HashMap dataRecord6 = new HashMap();
dataRecord6.put("id", "12");
dataRecord6.put("text", "齐齐哈尔");
dataRecord6.put("parentId", "1");
dataRecord6.put("money", "0");
HashMap dataRecord7 = new HashMap();
dataRecord7.put("id", "21");
dataRecord7.put("text", "长春");
dataRecord7.put("parentId", "2");
dataRecord7.put("money", "0");
HashMap dataRecord8 = new HashMap();
dataRecord8.put("id", "31");
dataRecord8.put("text", "鞍山");
dataRecord8.put("parentId", "3");
dataRecord8.put("money", "0");
HashMap dataRecord9 = new HashMap();
dataRecord9.put("id", "32");
dataRecord9.put("text", "辽阳");
dataRecord9.put("parentId", "3");
dataRecord9.put("money", "0");
HashMap dataRecord10 = new HashMap();
dataRecord10.put("id", "111");
dataRecord10.put("text", "道外区");
dataRecord10.put("parentId", "11");
dataRecord10.put("money", "0");
HashMap dataRecord11 = new HashMap();
dataRecord11.put("id", "112");
dataRecord11.put("text", "南岗区");
dataRecord11.put("parentId", "11");
dataRecord11.put("money", "0");
HashMap dataRecord12 = new HashMap();
dataRecord12.put("id", "121");
dataRecord12.put("text", "龙沙区");
dataRecord12.put("parentId", "12");
dataRecord12.put("money", "0");
HashMap dataRecord13 = new HashMap();
dataRecord13.put("id", "211");
dataRecord13.put("text", "二道区");
dataRecord13.put("parentId", "21");
dataRecord13.put("money", "0");
HashMap dataRecord14 = new HashMap();
dataRecord14.put("id", "212");
dataRecord14.put("text", "宽城区");
dataRecord14.put("parentId", "21");
dataRecord14.put("money", "0");
HashMap dataRecord15 = new HashMap();
dataRecord15.put("id", "311");
dataRecord15.put("text", "铁东区");
dataRecord15.put("parentId", "31");
dataRecord15.put("money", "0");
HashMap dataRecord16 = new HashMap();
dataRecord16.put("id", "312");
dataRecord16.put("text", "铁西区");
dataRecord16.put("parentId", "31");
dataRecord16.put("money", "0");
HashMap dataRecord17 = new HashMap();
dataRecord17.put("id", "321");
dataRecord17.put("text", "东京城");
dataRecord17.put("parentId", "32");
dataRecord17.put("money", "0");
HashMap dataRecord18 = new HashMap();
dataRecord18.put("id", "1111");
dataRecord18.put("text", "吴艳清家");
dataRecord18.put("parentId", "111");
dataRecord18.put("money", "4500");
HashMap dataRecord19 = new HashMap();
dataRecord19.put("id", "1112");
dataRecord19.put("text", "王泉历家");
dataRecord19.put("parentId", "111");
dataRecord19.put("money", "900");
HashMap dataRecord20 = new HashMap();
dataRecord20.put("id", "1121");
dataRecord20.put("text", "李磊家");
dataRecord20.put("parentId", "112");
dataRecord20.put("money", "890");
HashMap dataRecord21 = new HashMap();
dataRecord21.put("id", "1122");
dataRecord21.put("text", "李盛伟家");
dataRecord21.put("parentId", "112");
dataRecord21.put("money", "880");
HashMap dataRecord22 = new HashMap();
dataRecord22.put("id", "2111");
dataRecord22.put("text", "李雪涛家");
dataRecord22.put("parentId", "211");
dataRecord22.put("money", "3500");
HashMap dataRecord23 = new HashMap();
dataRecord23.put("id", "2112");
dataRecord23.put("text", "李润杰家");
dataRecord23.put("parentId", "211");
dataRecord23.put("money", "10000");
HashMap dataRecord24 = new HashMap();
dataRecord24.put("id", "2121");
dataRecord24.put("text", "李坤奇家");
dataRecord24.put("parentId", "212");
dataRecord24.put("money", "20000");
HashMap dataRecord25 = new HashMap();
dataRecord25.put("id", "2122");
dataRecord25.put("text", "窦晓峰家");
dataRecord25.put("parentId", "212");
dataRecord25.put("money", "10000");
HashMap dataRecord26 = new HashMap();
dataRecord26.put("id", "3111");
dataRecord26.put("text", "王琪跃家");
dataRecord26.put("parentId", "311");
dataRecord26.put("money", "8700");
HashMap dataRecord27 = new HashMap();
dataRecord27.put("id", "3112");
dataRecord27.put("text", "金城家");
dataRecord27.put("parentId", "311");
dataRecord27.put("money", "666");
HashMap dataRecord28 = new HashMap();
dataRecord28.put("id", "3121");
dataRecord28.put("text", "朱一家");
dataRecord28.put("parentId", "312");
dataRecord28.put("money", "13000");
HashMap dataRecord29 = new HashMap();
dataRecord29.put("id", "3122");
dataRecord29.put("text", "由建宏家");
dataRecord29.put("parentId", "312");
dataRecord29.put("money", "30000");
HashMap dataRecord30 = new HashMap();
dataRecord30.put("id", "3211");
dataRecord30.put("text", "李建保家");
dataRecord30.put("parentId", "321");
dataRecord30.put("money", "8600");
HashMap dataRecord31 = new HashMap();
dataRecord31.put("id", "3212");
dataRecord31.put("text", "龙明超家");
dataRecord31.put("parentId", "321");
dataRecord31.put("money", "20000");
HashMap dataRecord32 = new HashMap();
dataRecord32.put("id", "1211");
dataRecord32.put("text", "李涛家");
dataRecord32.put("parentId", "121");
dataRecord32.put("money", "7600");
HashMap dataRecord33 = new HashMap();
dataRecord33.put("id", "1212");
dataRecord33.put("text", "李龙波家");
dataRecord33.put("parentId", "121");
dataRecord33.put("money", "6000");
dataList.add(dataRecord1);
dataList.add(dataRecord2);
dataList.add(dataRecord3);
dataList.add(dataRecord4);
dataList.add(dataRecord5);
dataList.add(dataRecord6);
dataList.add(dataRecord7);
dataList.add(dataRecord8);
dataList.add(dataRecord9);
dataList.add(dataRecord10);
dataList.add(dataRecord11);
dataList.add(dataRecord12);
dataList.add(dataRecord13);
dataList.add(dataRecord14);
dataList.add(dataRecord15);
dataList.add(dataRecord16);
dataList.add(dataRecord17);
dataList.add(dataRecord18);
dataList.add(dataRecord19);
dataList.add(dataRecord20);
dataList.add(dataRecord21);
dataList.add(dataRecord22);
dataList.add(dataRecord23);
dataList.add(dataRecord24);
dataList.add(dataRecord25);
dataList.add(dataRecord26);
dataList.add(dataRecord27);
dataList.add(dataRecord28);
dataList.add(dataRecord29);
dataList.add(dataRecord30);
dataList.add(dataRecord31);
dataList.add(dataRecord32);
dataList.add(dataRecord33);
return dataList;
}
}
分页功能我就不再演示了,比较简单,只要每次取第一层的固定数目节点,然后循环调用toString()方法
就可以了。
三、思考与总结
这个H-Tree模型是一个理想化的模型,它适用于人口普查统计,对于其它的业务领域,这个模型可能不太
适用,比如银行,银行应该是一棵多叉树结构,并且不是等高的,每个分行下面即有支行又有存款的储户
,储户是叶子,支行是树枝,如图所示:
对于银行的统计分析又更加复杂一些了,到此结束吧,Game over!
四、联系方式
memorymultitree@163.com