该树主要是功能分为数据挂载和数据查找。
本文数据挂载以xml为例。
数据挂载主要根据数据的形式,找到根节点数据,并进行遍历展开。
root节点挂载文本信息,1级节点挂载root节点,顺次展开。
数据查找主要采用平级查找模式,如果在同一级叶子节点查找没有找到数据,就向下一层叶子进行查找。
最后附上代码。
Node.java
@Data
@RequiredArgsConstructor
public class Node implements Cloneable, Serializable {
private String id; //结点id
private String data; //结点数据
private List<Node> childrenList = new ArrayList<>(); //该结点的 子结点集合
private int nodeItemNum; // 树节点层高
private boolean checkMergeIdFlag = false;// 是否内ID相同
private boolean checkMergeValueFlag = false;// 是否内容相同
private Node parentNode;// 父节点
public Node(String id, int nodeItemNum) {
setId(id);
setNodeItemNum(nodeItemNum);
}
public Node(String id, String data, Node parentNode) {
setId(id);
setData(data);
setParentNode(parentNode);
}
public Node(String id, Node parentNode, int nodeItemNum) {
this(id, nodeItemNum);
setParentNode(parentNode);
}
public Node(String id, String data, Node parentNode, int nodeItemNum) {
this(id, data, parentNode);
setNodeItemNum(nodeItemNum);
}
public Node(String id, String data, int nodeItemNum) {
this(id, nodeItemNum);
setData(data);
}
// 添加孩子节点
public void addChildrenNode(Node node) {
childrenList.add(node);
}
@Override
protected Node clone() {
Node node = new Node(id, data, parentNode, nodeItemNum);
node.setCheckMergeIdFlag(checkMergeIdFlag);
node.setCheckMergeValueFlag(checkMergeValueFlag);
ListIterator<Node> nodeListIterator = node.getChildrenList().listIterator();
while (nodeListIterator.hasNext()) {
node.addChildrenNode(nodeListIterator.next().clone());
}
return node;
}
protected Node clone(int nodeItemNum, Node parentNode) {
Node node = new Node(id, data, parentNode, nodeItemNum);
ListIterator<Node> nodeListIterator = node.getChildrenList().listIterator();
while (nodeListIterator.hasNext()) {
node.addChildrenNode(nodeListIterator.next().clone(nodeItemNum++, node));
}
return node;
}
public void updateData(Node newNode) {
setData(newNode.getData());
}
public boolean checkChildren(Node childrenNode) {
return getChildrenList().stream().allMatch(node -> node.getId().equals(childrenNode.getId()) && node.getData().equals(childrenNode.getData()));
}
@Override
public String toString() {
return "Node{" + "id='" + id + '\'' + ", data='" + data + '\'' + ", nodeItemNum=" + nodeItemNum + ", checkMergeIdFlag=" + checkMergeIdFlag + ", checkMergeValueFlag=" + checkMergeValueFlag + ", parentNode=" + parentNode + '}';
}
public void updateKey() {
if (id.equals("root") || nodeItemNum == 1) {
ListIterator<Node> nodeListIterator = getChildrenList().listIterator();
while (nodeListIterator.hasNext()) {
nodeListIterator.next().updateKey();
}
} else {
List<TreeAttribute> treeAttributes = JSONUtil.toList(id, TreeAttribute.class);
Optional<TreeAttribute> attribute = treeAttributes.stream().filter(treeAttribute -> treeAttribute.getKey().equals("keyId")).findFirst();
setId(attribute.get().getValue());
ListIterator<Node> nodeListIterator = getChildrenList().listIterator();
while (nodeListIterator.hasNext()) {
nodeListIterator.next().updateKey();
}
}
}
}
这是节点类,节点信息保存
MultiwayTree.java
public abstract class MultiwayTree {
@Getter
Node root = new Node("root", 0); //树的根节点
/**
* 查询
*
* @return
*/
public Node loopById(String nodeId) {
return loopById(nodeId, getRoot(), 0);
}
/**
* 查询
*
* @return
*/
public Node loopById(String nodeId, Node parentNode, int nodeItemNum) {
Node node = null;
if (nodeId.equals(parentNode.getId())) {
node = parentNode;
} else {
nodeItemNum = nodeItemNum == 0 ? parentNode.getNodeItemNum() : nodeItemNum;
List<Node> nodes = loopForItemNum(nodeItemNum, parentNode);
for (Node nodeItem : nodes) {
if (nodeId.equals(nodeItem.getId())) {
node = nodeItem;
break;
}
}
if (node == null) {
nodeItemNum += 1;
node = loopById(nodeId, parentNode, nodeItemNum);
}
}
return node;
}
/**
* 查询
*
* @return
*/
public Node loopById(String nodeId, Node parentNode, int nodeItemNum, int index) {
Node node = null;
nodeItemNum = nodeItemNum == 0 ? parentNode.getNodeItemNum() : nodeItemNum;
List<Node> nodeList = loopForItemNum(nodeItemNum, parentNode);
for (int i = 0; i < nodeList.size(); i++) {
Node nodeItem = nodeList.get(i);
if (i == index && nodeId.equals(nodeItem.getId())) {
node = nodeItem;
break;
}
}
return node;
}
/**
* 获取同层级所有node
*
* @param nodeItemNum
* @return
*/
public List<Node> loopForItemNum(int nodeItemNum) {
return loopForItemNum(nodeItemNum, getRoot());
}
/**
* 获取同层级所有node
*
* @param nodeItemNum
* @return
*/
public List<Node> loopForItemNum(int nodeItemNum, Node node) {
List<Node> nodeList = new ArrayList<>();
for (Node childrenNode : node.getChildrenList()) {
if (childrenNode.getNodeItemNum() < nodeItemNum) {
nodeList.addAll(loopForItemNum(nodeItemNum, childrenNode));
} else if (childrenNode.getNodeItemNum() == nodeItemNum) {
nodeList.add(childrenNode);
} else {
nodeList.add(node);
}
}
return nodeList;
}
/**
* 插入新结点 输入父结点id进行定位 ,将新结点 插入到父结点的 sonList 中
*
* @param fatherId 新结点的 父id
* @param newNode 新结点
*/
public void insert(String fatherId, Node newNode) {
loopById(fatherId, getRoot(), 0).addChildrenNode(newNode);
}
/**
* 删除结点 注意:先判断 是否在删除 根结点. 若删除根结点,不必进入此方法 直接为null即可
*
* @param changeNode 根结点
* @param id 待删除结点id
*/
public void delete(Node changeNode, String id) {
List<Node> sonList = changeNode.getChildrenList();
if (sonList == null || sonList.isEmpty()) {
return;
} else {
for (int i = 0; i < sonList.size(); i++) {
if (id.equals(sonList.get(i).getId())) {
sonList.remove(i);
delete(new Node(), id);
break;
} else {
delete(sonList.get(i), id);
}
}
}
}
/**
* 根据结点id 修改结点 name //同理可根据结点name修改结点id
*
* @param changeNode 根结点
* @param id 结点id
* @param data 修改后的 新data
*/
public void update(Node changeNode, String id, String data) {
if (changeNode.getId().equals(id)) {
changeNode.setData(data);
return;
}
List<Node> sonList = changeNode.getChildrenList();
if (sonList == null || sonList.isEmpty()) {
return;
} else {
for (int i = 0; i < sonList.size(); i++) {
update(sonList.get(i), id, data);
}
}
}
/**
* 查询 某个结点 到根结点的路径
*
* @param changeNode 根结点
* @param data 待查找的结点 name
* @param wayList 路径
*/
public void queryWayByData(Node changeNode, String data, ArrayList<String> wayList) {
List<Node> sonList = changeNode.getChildrenList();
wayList.add(changeNode.getData());
if (sonList == null || sonList.isEmpty()) {
return;
} else {
for (int i = 0; i < sonList.size(); i++) {
if (data.equals(sonList.get(i).getData())) {
for (int j = 0; j < wayList.size(); j++) {
System.out.print(wayList.get(j) + "->");
}
System.out.println(sonList.get(i).getData());
break;
}
queryWayByData(sonList.get(i), data, wayList);
}
}
}
多叉树类
TreeData.java
public class TreeData extends MultiwayTree {
//添加方法重载
public void add(String parentId, String nodeId, String data) {
add(loopById(parentId), nodeId, data);
}
//添加
public Node add(Node parentNode, String nodeId, String data) {
Node newNode = new Node(nodeId, data, parentNode, parentNode.getNodeItemNum() + 1);
parentNode.addChildrenNode(newNode);
return newNode;
}
//添加
public Node add(Node parentNode, Node copyNode) {
return copyNode.clone(parentNode.getNodeItemNum() + 1, parentNode);
}
// 清空数据
public void clear() {
getRoot().getChildrenList().clear();
}
/**
* 合并
*
* @param oldNodeRoot
* @param newNodeRoot
*/
public void merge(Node oldNodeRoot, Node newNodeRoot) {
int nodeItemNum = oldNodeRoot.getNodeItemNum() == oldNodeRoot.getNodeItemNum() ? oldNodeRoot.getNodeItemNum() : 0;
while (loopForItemNum(nodeItemNum, oldNodeRoot).size() > 0) {
List<Node> oldNodeList = loopForItemNum(nodeItemNum, oldNodeRoot);
List<Node> newNodeList = loopForItemNum(nodeItemNum, newNodeRoot);
for (int index = 0; index < oldNodeList.size(); index++) {
Node oldNode = oldNodeList.get(index);
if (oldNode.isCheckMergeIdFlag() == false) {
// 查看是旧节点是否在新树中存在,如果不存在,就删除
if (newNodeList.stream().noneMatch(newChildNode -> newChildNode.getId().equals(oldNode.getId()))) {
delete(oldNode.getParentNode(), oldNode.getId());
}
}
// 如果值不相同,就合并
if (oldNode.isCheckMergeIdFlag() == true && oldNode.isCheckMergeValueFlag() == false) {
oldNode.updateData(loopById(oldNode.getId(), newNodeRoot, nodeItemNum, index));
}
}
for (int i = 0; i < newNodeList.size(); i++) {
Node newNode = newNodeList.get(i);
if (newNode.isCheckMergeIdFlag() == false) {
List<Node> parentNodeList = loopForItemNum(newNode.getParentNode().getNodeItemNum(), newNodeRoot);
int parentNodeIndex = 0;
for (int i1 = 0; i1 < parentNodeList.size(); i1++) {
if (parentNodeList.get(i1) == newNode.getParentNode()) {
parentNodeIndex = i1;
}
}
// 旧树中 添加 新节点
Node parentNode = loopById(newNode.getParentNode().getId(), oldNodeRoot, newNode.getParentNode().getParentNode().getNodeItemNum(), parentNodeIndex);
if (null != parentNode) {
add(parentNode, newNode.getId(), newNode.getData());
}
}
}
nodeItemNum += 1;
}
}
/**
* 如果id相同,说明节点存在;如果值相同,说明两次结果相同
*
* @param oldNodeRoot
* @param newNodeRoot
*/
public void checkNode(Node oldNodeRoot, Node newNodeRoot) {
int nodeItemNum = oldNodeRoot.getNodeItemNum() == newNodeRoot.getNodeItemNum() ? oldNodeRoot.getNodeItemNum() : 1;
loopForItemNum(nodeItemNum);
while (loopForItemNum(nodeItemNum).size() > 0) {
List<Node> oldNodeList = loopForItemNum(nodeItemNum, oldNodeRoot);
List<Node> newNodeList = loopForItemNum(nodeItemNum, newNodeRoot);
for (int i = 0; i < oldNodeList.size(); i++) {
Node oldNode = oldNodeList.get(i);
Node newNode = i > newNodeList.size() - 1 ? null : newNodeList.get(i);
//父级节点不同的时候,子节点不再比较
if (null != oldNode.getParentNode() && !oldNode.getParentNode().isCheckMergeIdFlag()) continue;
if (null != newNode && oldNode.getId().equals(newNode.getId())) {
oldNode.setCheckMergeIdFlag(true);
newNode.setCheckMergeIdFlag(true);
if (oldNode.getData().equals(newNode.getData())) {
oldNode.setCheckMergeValueFlag(true);
newNode.setCheckMergeValueFlag(true);
}
} else {
Optional<Node> newNodeStream = newNodeList.stream().filter(newNodeTtem -> oldNode.getId().equals(newNodeTtem.getId())).findFirst();
if (newNodeStream.isPresent()) {
oldNode.setCheckMergeIdFlag(true);
newNodeStream.get().setCheckMergeIdFlag(true);
if (oldNode.getData().equals(newNodeStream.get().getData())) {
oldNode.setCheckMergeValueFlag(true);
newNodeStream.get().setCheckMergeValueFlag(true);
}
}
}
}
nodeItemNum += 1;
}
}
/**
* 加载数据
*
* @param treeMap
* @param parentNode
*/
public void loadTreeMap(TreeMap<String, Object> treeMap, Node parentNode) {
Node newNode = null;
for (Map.Entry<String, Object> treeMapEntry : treeMap.entrySet()) {
if (treeMapEntry.getValue() instanceof String) {
newNode = add(parentNode, treeMapEntry.getKey(), String.valueOf(treeMapEntry.getValue()));
} else if (treeMapEntry.getValue() instanceof ArrayList) {
ArrayList<TreeMap<String, Object>> treeMapList = (ArrayList<TreeMap<String, Object>>) treeMapEntry.getValue();
for (TreeMap<String, Object> treeMapItem : treeMapList) {
loadTreeMap(treeMapItem, newNode);
}
}
}
}
/**
* 遍历结点 并打印. 同时按每个结点所在深度 在结点前打印不同长度的空格
*
* @param changeNode 根结点
* @param depth 结点深度:1
*/
public void queryAll(Node changeNode, int depth) {
List<Node> sonList = changeNode.getChildrenList();
String space = generateSpace(depth); //根据深度depth,返回(depth*3)长度的空格
if (sonList == null || sonList.isEmpty()) {
return;
}
for (int i = 0; i < sonList.size(); i++) {
System.out.println(space + "---" //打印空格 和结点id,name
+ "<" + sonList.get(i).getId() + ">" + sonList.get(i).getData());
if (i == 0) {
depth = depth + 1; //结点深度+1,每个for循环仅执行一次。作为子结点sonList.get(i)的深度
}
queryAll(sonList.get(i), depth);
}
}
//打印空格
public static String generateSpace(int count) {
count = count * 3;
char[] chs = new char[count];
for (int i = 0; i < count; i++) {
chs[i] = ' ';
}
return new String(chs);
}
}
数据结构和数据方法实现类
service类
TreeService.java
public interface TreeService {
/**
* 读取 文件夹下所有文件
*
* @param oldFileList
* @param newFileList
* @throws Exception
*/
void readDirToTree(List<File> oldFileList, List<File> newFileList) throws Exception;
/**
* 读取文件
*
* @param oldFilePath
* @param newFilePath
* @throws Exception
*/
void readXmlToTree(String oldFilePath, String newFilePath) throws Exception;
/**
* 合并数据
*/
void merge();
/**
* 保存
*/
void save();
/**
* 打印差异报表
*
* @param reportFilePath
*/
void printDifferenceReport(String reportFilePath);
服务类
TreeServiceImpl.java
@Slf4j
@Service
public class TreeServiceImpl implements TreeService {
@Autowired
Data data;
@Override
public void readDirToTree(List<File> oldFileList, List<File> newFileList) throws Exception {
List<TreeData> oldTreeDataList = new ArrayList<>();
List<TreeData> newTreeDataList = new ArrayList<>();
for (File oldFile : oldFileList) {
oldTreeDataList.add(ParseXML.parserXmlTree(oldFile));
}
for (File newFile : newFileList) {
newTreeDataList.add(ParseXML.parserXmlTree(newFile));
}
data.insertOldData(oldTreeDataList);
data.insertNewData(newTreeDataList);
data.checkDifference();
}
@Override
public void readXmlToTree(String oldFilePath, String newFilePath) throws Exception {
data.insertOldData(new ArrayList<>(Collections.singletonList(ParseXML.parserXmlTree(new File(oldFilePath)))));
data.insertNewData(new ArrayList<>(Collections.singletonList(ParseXML.parserXmlTree(new File(newFilePath)))));
data.checkDifference();
}
@Override
public void merge() {
data.merge();
data.checkDifference();
}
@Override
public void save() {
for (TreeData treeData : data.getOldData()) {
treeData.getRoot().updateKey();
TemplateModel templateModel = CommonUtils.treeConvertedTempBean(treeData);
FreemarkerUtil.templateWriterXml(templateModel);
}
}
@Override
public void printDifferenceReport(String reportFilePath) {
log.info("打印差异报表: " + reportFilePath);
}
服务实现类
Data.java
@Component
public class Data {
// 原文件树结构
@Getter
private List<TreeData> oldData = new ArrayList<>();
// 合并文件树结构
private List<TreeData> newData = new ArrayList<>();
/**
* 清空数据
*/
public void clear() {
oldData.clear();
newData.clear();
}
public void insertOldData(List<TreeData> treeMapList) {
oldData.addAll(treeMapList);
}
public void insertNewData(List<TreeData> treeMapList) {
newData.addAll(treeMapList);
}
/**
* 检索树 不同地方并标记
*/
public void checkDifference() {
for (TreeData oldDatum : oldData) {
Optional<TreeData> newDatum = newData.stream().filter(newTreeData -> newTreeData.getRoot().getData().equals(oldDatum.getRoot().getData())).findFirst();
oldDatum.checkNode(oldDatum.getRoot(), newDatum.get().getRoot());
}
}
/**
* 合并
*/
public void merge() {
for (TreeData oldDatum : oldData) {
Optional<TreeData> newDatum = newData.stream().filter(newTreeData -> newTreeData.getRoot().getData().equals(oldDatum.getRoot().getData())).findFirst();
oldDatum.merge(oldDatum.getRoot(), newDatum.get().getRoot());
}
}
数据库类
写了好几年程序,后来才发现,数据结构和设计模式才是重中之重。
有用就先点赞收藏吧。