package org.jeecg.modules.dnc.utils; import it.unimi.dsi.fastutil.longs.Long2IntMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import org.jeecg.modules.dnc.entity.ProductMix; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Collectors; public class TreeBuilder { //========================= 数据预处理 =========================// public CleanResult preprocessData(List rawList) { // 阶段1:构建线程安全的节点映射 ConcurrentHashMap tempMap = (ConcurrentHashMap) rawList.parallelStream() .collect(Collectors.toConcurrentMap( ProductMix::getId, item -> item, (existing, replacement) -> existing )); // 转换为FastUtil结构以优化内存 Long2ObjectOpenHashMap nodeMap = new Long2ObjectOpenHashMap<>(tempMap.size()); nodeMap.putAll(tempMap); // 阶段2:过滤有效节点 List validNodes = rawList.parallelStream() .filter(item -> { if (item.getParentId() == 0) return true; // 根节点直接通过 if (!nodeMap.containsKey(item.getParentId())) { System.err.println("无效父节点引用: ID=" + item.getId() + " 父ID=" + item.getParentId()); return false; } return true; }) .collect(Collectors.toList()); return new CleanResult(nodeMap, validNodes); } //========================= 拓扑排序 =========================// public List topologicalSort(List nodes, Long2ObjectMap nodeMap) { // 防御空数据 if (nodes.isEmpty()) return Collections.emptyList(); // 1. 构建邻接表(父节点 -> 子节点列表) Long2ObjectMap> adjacencyList = new Long2ObjectOpenHashMap<>(nodes.size()); nodes.forEach(node -> { Long parentId = node.getParentId(); adjacencyList.computeIfAbsent(parentId, k -> new ArrayList<>()) .add(node.getId()); }); // 2. 初始化入度计数器 Long2IntMap inDegree = new Long2IntOpenHashMap(); nodes.forEach(node -> inDegree.put(node.getId().longValue(), 0)); // 3. 计算入度 nodes.forEach(node -> { if (node.getParentId() != 0 && nodeMap.containsKey(node.getParentId())) { inDegree.put(node.getId().longValue(), inDegree.get(node.getId()) + 1); } }); // 4. 初始化队列 Queue queue = new ConcurrentLinkedQueue<>( nodes.stream() .filter(node -> inDegree.get(node.getId()) == 0) .map(ProductMix::getId) .collect(Collectors.toList()) ); // 5. 拓扑排序核心逻辑 List sorted = new ArrayList<>(nodes.size()); while (!queue.isEmpty()) { Long currentId = queue.poll(); ProductMix current = nodeMap.get(currentId); sorted.add(current); List children = adjacencyList.getOrDefault(currentId, Collections.emptyList()); children.forEach(childId -> { int degree = inDegree.get(childId) - 1; inDegree.put(childId, Integer.valueOf(degree)); if (degree == 0) { queue.offer(childId); } }); } // 6. 循环检测 if (sorted.size() != nodes.size()) { handleSortingFailure(nodes, sorted, nodeMap); return Collections.emptyList(); } return sorted; } //========================= 树结构组装 =========================// public List assembleTree(List sortedNodes, Long2ObjectMap nodeMap) { // 1. 创建节点副本 Map treeMap = new ConcurrentHashMap<>(sortedNodes.size()); sortedNodes.forEach(node -> { ProductMix newNode = new ProductMix( node.getId(), node.getParentId(), node.getName(), node.getCode(), node.getType() ); treeMap.put(newNode.getId(), newNode); }); // 2. 建立父子关系 sortedNodes.forEach(node -> { if (node.getParentId() != 0) { ProductMix parent = treeMap.get(node.getParentId()); if (parent != null) { parent.getChildren().add(treeMap.get(node.getId())); } } }); // 3. 返回根节点 return treeMap.values().stream() .filter(node -> node.getParentId() == 0) .collect(Collectors.toList()); } //========================= 辅助方法 =========================// private void handleSortingFailure(List originalNodes, List sortedNodes, Long2ObjectMap nodeMap) { // 查找未处理的节点ID Set processedIds = sortedNodes.stream() .map(ProductMix::getId) .collect(Collectors.toSet()); List unprocessed = originalNodes.stream() .filter(node -> !processedIds.contains(node.getId())) .collect(Collectors.toList()); System.err.println("拓扑排序失败,未处理节点数: " + unprocessed.size()); detectCycles(unprocessed, nodeMap); } private void detectCycles(List nodes, Long2ObjectMap nodeMap) { Set visited = new HashSet<>(); nodes.forEach(node -> { Long currentId = node.getId(); Set path = new LinkedHashSet<>(); while (currentId != 0) { if (!path.add(currentId)) { System.err.println("循环路径: " + path); return; } ProductMix current = nodeMap.get(currentId); if (current == null) break; currentId = current.getParentId(); } visited.addAll(path); }); } //========================= 数据结构 =========================// public static class CleanResult { private final Long2ObjectMap nodeMap; private final List validNodes; public CleanResult(Long2ObjectMap nodeMap, List validNodes) { this.nodeMap = nodeMap; this.validNodes = validNodes; } public Long2ObjectMap getNodeMap() { return nodeMap; } public List getValidNodes() { return validNodes; } } }