在java 中树形列表信息的查询
在IT的项⽬开发中,有时我们会经常遇到树形列表数据的查询等的相关操作,⽐如下⾯的情况就是如此:
传统的遍历⽅式
树形列表数据最基本的展⽰⼀共需要三个字段,分别是id,pId以及name。其中id是该条信息的主键,⼀般是⾃增的,并且不允许重复。pId是该条信息所归属的⽗节点的id,该pId允许重复,⽽顶级的节点其由于没有⽗节点,故其⽗节点字段往往以0进⾏标识。⽽name就是该条数据所展⽰的名称字段。
传统的树形列表数据的查询⽅式是先查询pId为0的数据,然后以该数据的id作为⼦节点pId的查询条件,⽤此来查询其直接的⼦节点数据;再以此⼦节点的id作为下⼀层数据的pId查询条件,依次类推,逐渐完成整个树形列表数据的查询操作。
源码如下:
Tree TraditionTest
package com.ity;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@Builder
@ToString
public class Tree {
private int id;
private String name;
private int pId;
private String level;}
package com.;
import utils.ListContainer;
import llect.Lists;
import com.ity.Tree;
import slf4j.Slf4j;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
import java.util.List;
/**
* 传统的查找⽅式
*/
@Slf4j
public class TraditionTest {
private ListContainer<Tree> treeContainer = wListContainer();
/**
* 链式调⽤添加数据
*/
@Before
public void initDB(){
Tree tree = Tree.builder()
.id(1)
早春呈水部张十八员外
.name("0")
.pId(0)
.build();
Tree tree1 = Tree.builder()
.id(2)
.name("0-1")
.pId(1)
.build();
Tree tree2 = Tree.builder()
.id(3)
.name("0-2")
.pId(1)
.build();
Tree tree3 = Tree.builder()
.
id(4)
.name("0-1-1")
.pId(2)
.build();陈从容
treeContainer.add(tree).add(tree1).add(tree2).add(tree3);
}
@Test
public void test(){
List<Tree> treeList = List();
long start = new DateTime().getMillis();
int i = 10000;
while(i > 0){
//遍历树节点
traver(treeList);
i --;
}
long end = new DateTime().getMillis();
log.info("传统遍历耗时:" + (end - start));
}
/**
* 遍历树节点
* @param treeList
*/
public void traver(List<Tree> treeList){
List<Tree> rootList = wArrayList();
treeList.forEach(tree -> {
//打印出⽗节点
PId() == 0){
/*log.String());*/
rootList.add(tree);
}
神州场站
});
//从根节点开始循环遍历树节点
/
/从根节点开始循环遍历树节点
rootList.forEach(tree -> {
traverChildren(tree,treeList);
});
}
/**
* 递归调⽤,不断的循环遍历
* @param tree
* @param treeList
*/
public void traverChildren(Tree tree,List<Tree> treeList){
treeList.forEach(tree1 -> {
PId() == Id()){
/*log.String());*/
//进⾏循环递归调⽤
traverChildren(tree1,treeList);
}
});
}
}
快速遍历
快速遍历的原理就是根据所传⼊的节点来查询该节点下的所有⼦节点信息,其中⽤到了⼀个关键的
字段就是level,该字段显⽰的是当前的信息层级,根据level我们可以快速的查询该层级下的所有⼦节点信息,其中⽤到了另⼀个关键的容器,那就是Multimap,其特点就是允许在其中以Map,但是主键可以重复的⽅式存放信息。这样我们如果想获取某⼀层级下的⼦节点集合,我们直接调⽤()⽅法即可,这样就省去了传统的不断循环遍历的操作。
源码如下:
QuickTest
package com.;
import utils.ListContainer;
llect.ArrayListMultimap;
llect.Lists;
llect.Multimap;
import com.ity.Tree;
slf4j.Slf4j;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
/**
* 快速查找
* 两种查找的执⾏耗时⽐较
* 传统快速
* 140 156
* 124 125
* 173 234
* 结论,如果要是执⾏遍历操作,那么采⽤传统的⽅式耗时更短
* 但是快速查找之所以被称之为快速,其主要的原因在于查找某⼀个节点下的所有⼦节点(⾮根节点,同时树中的节点⽐较多的情况下)
* 快速查找由于有辅助字段level的帮助,每⼀次的查找,其范围都要缩⼩⼀个层级,但是传统的查找并不能这样,传统的查找⽅式仍然
* 是每次查找⼀个节点,都要将全部的节点都扫描⼀遍,因⽽在这种情况下,快速查找⽅式要优于传统的查找⽅式。
* 因⽽在树形列表的查询使⽤中,推荐使⽤快速查找⽅式。
*/
@Slf4j
public class QuickTest {
private ListContainer<Tree> treeContainer = wListContainer();
/**
* 链式调⽤添加数据
*/
@Before
public void initDB(){
Tree tree = Tree.builder()
.id(1)
.name("0")
.pId(0)
.level("0")
.
特工韩国电影build();
Tree tree1 = Tree.builder()
.id(2)
.name("0-1")
.pId(1)
.level("0.1")
.build();
Tree tree2 = Tree.builder()
.id(3)
.name("0-2")
.pId(1)
.
level("0.1")
.build();
Tree tree3 = Tree.builder()
.id(4)
.name("0-1-1")
.pId(2)
.level("0.1.2")
.build();
treeContainer.add(tree).add(tree1).add(tree2).add(tree3);
}
@Test
ps液化快捷键
public void test(){
List<Tree> treeList = List();
Tree tree = Tree.builder()
.id(1)
.name("0")
.pId(0)
.level("0")
.build();
long start = new DateTime().getMillis();
int i = 10000;
while(i > 0){
/
/遍历树节点
traver(tree,treeList);
/*log.String());*/
柔和的反义词i --;
}
long end = new DateTime().getMillis();
log.info("快速遍历耗时:" + (end - start));
}
/**
* 遍历树节点
* @param treeList
*/
public void traver(Tree rootTree, List<Tree> treeList){
List<Tree> rootList = wArrayList();
Multimap<String,Tree> multimap = ate();
treeList.forEach(tree -> {
//将treeList中的信息放⼊到multimap容器中
Level().indexOf(getNextLevel(rootTree)) != -1){
Level().indexOf(getNextLevel(rootTree)) != -1){
multimap.Level(),tree);
}
Level().equals(getNextLevel(rootTree))){
rootList.add(tree);
}
});
//从根节点开始循环遍历树节点
rootList.forEach(tree -> {
滑鱼片/*log.String());*/
traverChildren(tree,multimap);
});
}
/**
* 递归调⽤,不断的循环遍历
* @param rootTree2017国考报名
* @param multimap
*/
public void traverChildren(Tree rootTree, Multimap<String,Tree> multimap){
String nextLevel = getNextLevel(rootTree);
List<Tree> treeList = (List<Tree>) (nextLevel);
Multimap<String,Tree> multimapChildren = ate();
List<Tree> rootList = wArrayList();
treeList.forEach(tree1 -> {
//将treeList中的信息放⼊到multimap容器中
Level().indexOf(getNextLevel(rootTree)) != -1){
multimapChildren.Level(),tree1);
}
Level().equals(getNextLevel(rootTree))){
rootList.add(tree1);
}
});
//从根节点开始循环遍历树节点
rootList.forEach(tree -> {
/*log.String());*/
traverChildren(tree,multimapChildren);
});
}
/**
* 获取下⼀层级的level
* @param tree
* @return
*/
private String getNextLevel(Tree tree){
Level() + "." + Id();
}
}
执⾏耗时⽐价
以简单的遍历所有的节点为例:
执⾏次数传统快速⼀140156
⼆124125
三173234分析