项目中需要做一个地区选择插件,由于地区之间的关系为树形结构,所以我们可以用多叉树来存储地区数据,并将多叉树转为json字符串前台做处理。
首先,在实现代码介绍之前我们需要了解,什么是树以及树的结构。
以下摘选自百度百科
树(tree)是包含n(n>=0)个结点的有穷集,其中:
(1)每个元素称为结点(node);
(2)有一个特定的结点被称为根结点或树根(root)。
(3)除根结点之外的其余数据元素被分为m(m≥0)个互不相交的集合T1,T2,……Tm-1,其中每一个集合Ti(1<=i<=m)本身也是一棵树,被称作原树的子树(subtree)。
树也可以这样定义:树是由根结点和若干颗子树构成的。树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构。在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点,或称为树根。
树结构图形如下
多叉树也多被叫为b树
在了解了树的结构之后,我们就可以根据树的基本模型来进行java代码的实现。
不管是root节点还是叶子节点,都包含着基本的结构,节点数据、父节点、当前节点及子节点指针。由各个节点通过子节点指针关联起来就形成的一颗b树。那么我们就先定义对象来存储节点。
public class Node {
//节点id
public String id;
//父节点id
public String pid;
//节点数据
public String content;
//子节点指针集合
List<Node> nodes=null;
}
创建一个树结构对象,存储数据
public class Tree {
private Node node=null;
private List<Map> datas=null;
}
而完成一个树型结构,最主要的步骤:
第一步,构建父节点
//搜索父节点
Map t=FindDatasById(id);
//初始化父节点
this.node=new Node(t);
第二步,根据根节点数据,搜索对应子节点插入到父节点中。
//递归初始化节点数据
init(this.node);
//初始化树
private void init(Node node){
for(Map temp:FindListDatasByPId(node.getId())){//搜索子节点数据
Node tempNode=new Node(temp);
init(tempNode); //初始化子节点
node.nodes.add(tempNode); //将子节点加入当前父节点
}
}
实现逻辑:
- 找到当前节点下的所有子节点。
- 对子节点循环,将所有子节点初始化。
- 将已初始化的子节点加入当前节点。
基本一个b树的数据就被保存到咱们的Tree对象中了
而在项目用到的时候需要用该树做一个下拉选地区的一个功能,那么我们还需要将该树生成为json格式的数据,代码如下:
private static String NODE_ID="id";
private static String NODE_PID="pid";
private static String NODE_NAME="name";
initJsonStr(this.node);
public void initJsonStr(Node node){
Node tempNode=node;
if(node.getId()!=node.getId()){
jsonStr.append(",");
}
jsonStr.append("{").
append("\""+NODE_ID+"\":\"").append(tempNode.getId()).append("\",").
append("\""+NODE_NAME+"\":\"").append(tempNode.getContent()).append("\",").
append("\""+NODE_PID+"\":\"").append(tempNode.getPid()).append("\"");
if(isHaveLeafWithPid(tempNode)){
jsonStr.append(",\"nodes\":[");
List<Node> tempnodes=tempNode.nodes;
for(int i=0;i<tempnodes.size();i++) {
initJsonStr(tempnodes.get(i));
if(tempnodes.size()-1!=i){
jsonStr.append(",");
}
}
jsonStr.append("]");
}
jsonStr.append("}");
}
跟构建树的过程相类似,也是使用了递归的操作,请参照树节点构造步骤。
数据量大时’符号可能会让json字符无法被识别,所以这里采用”来构建json对象属性键值对。
数据库中,表结构数据至少包含为 id,pid,name。
下面贴出完整代码
//Node
package com.fenghao.sys.pojo;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Node {
public String id;
public String pid;
public String content;
List<Node> nodes=null;
public Node(){
this.nodes=new ArrayList<>();
}
public Node(String id,String pid,String content){
this.id=id;
this.pid=pid;
this.content=content;
if(nodes==null){
nodes=new ArrayList<>();
}
}
public Node(Map map){
System.out.println(map);
id=(String) map.get("id");
pid=(String) map.get("pid");
content=(String) map.get("name");
if(nodes==null){
nodes=new ArrayList<>();
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public void setNode(Node node){
this.nodes.add(node);
}
public List<Node> getNodes(){
return this.nodes;
}
}
//Tree
package com.fenghao.sys.pojo;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Tree {
private Node node=null;
private List<Map> datas=null;
private StringBuffer jsonStr=new StringBuffer();
private static String NODE_ID="id";
private static String NODE_PID="pid";
private static String NODE_NAME="name";
private Tree(List<Map> datas){
this.datas=datas;
}
public static Tree getInstance(List<Map> datas,String id){
Tree temp= new Tree(datas);
try {
temp.init(id);
return temp;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public String getJSONString(){
return jsonStr.toString();
}
//初始化单树父节点
private void init(String id) throws Exception{
//搜索父节点
Map t=FindDatasById(id);
if(null==t){//假如搜索的父节点id数据不存在,抛出异常
throw new Exception();
}
//初始化父节点
this.node=new Node(t);
init(this.node);
initJsonStr(this.node);
}
//初始化树
private void init(Node node){
String id=node.getId();
for(Map temp:FindListDatasByPId(id)){
//假如遇到节点id为空或者节点数据为空的,跳过本次循环
if(isEmpty(temp.get(NODE_ID))||isEmpty(temp.get(NODE_NAME))){
continue;
}
Node tempNode=new Node(temp);
init(tempNode); //初始化子节点
node.nodes.add(tempNode); //将子节点加入当前父节点
}
}
public void initJsonStr(Node node){
Node tempNode=node;
if(node.getId()!=node.getId()){
jsonStr.append(",");
}
jsonStr.append("{").
append("\""+NODE_ID+"\":\"").append(tempNode.getId()).append("\",").
append("\""+NODE_NAME+"\":\"").append(tempNode.getContent()).append("\",").
append("\""+NODE_PID+"\":\"").append(tempNode.getPid()).append("\"");
if(isHaveLeafWithPid(tempNode)){
jsonStr.append(",\"nodes\":[");
List<Node> tempnodes=tempNode.nodes;
for(int i=0;i<tempnodes.size();i++) {
initJsonStr(tempnodes.get(i));
if(tempnodes.size()-1!=i){
jsonStr.append(",");
}
}
jsonStr.append("]");
}
jsonStr.append("}");
}
//判断父节点下是否有子节点
public boolean isHaveLeafWithPid(String pid){
for(int i=0;i<this.datas.size();i++){
Map temp=this.datas.get(i);
if(temp.get(NODE_PID)!=null&&temp.get(NODE_PID).toString().equals(pid)){
return true;
}
}
return false;
}
//判断父节点下是否有子节点
public boolean isHaveLeafWithPid(Node node){
if(node.nodes!=null&&node.nodes.size()>0){
return true;
}
return false;
}
//找到父节点下的所有子节点
private List<Map> FindListDatasByPId(String pid){
List<Map> map=new ArrayList<>();
for(int i=this.datas.size()-1;i>=0;i--){
Map temp=this.datas.get(i);
if(temp.get(NODE_PID)!=null&&temp.get(NODE_PID).toString().equals(pid)){
this.datas.remove(i);
map.add(temp);
}
}
return map;
}
//找到父节点
private Map FindDatasById(String id){
for(int i=0;i<this.datas.size();i++){
Map temp= this.datas.get(i);
if(temp.get(NODE_ID).toString().equals(id)){
this.datas.remove(i);
return temp;
}
}
return null;
}
private boolean isEmpty(Object obj){
if(null==obj||"".equals(obj)){
return true;
}
return false;
}
}
//test.java
package test;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.slf4j.Logger;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-datasource.xml","classpath:spring-mybatis.xml"})
public class Testmybatis {
@Autowired
SqlSessionFactory sqlSessionFactory;
public Logger logger=LoggerFactory.getLogger(this.getClass());
@Test
public void test2(){
SqlSession session = sqlSessionFactory.openSession();
List<Map> result= session.selectList("area.selectArea");
Tree temp=Tree.getInstance(result,"370000");
logger.info(temp.getJSONString());
}
}
粘贴部分数据
{name=潍坊市, pid=370000, id=370700}
{name=昌邑市, pid=370700, id=370786}
{name=高密市, pid=370700, id=370785}
{name=安丘市, pid=370700, id=370784}
{name=寿光市, pid=370700, id=370783}
//sql
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="area">
<select id="selectArea" resultType="Map" parameterType="java.lang.String">
select DISTINCT id, pid,name from table
</select>
</mapper>