mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
refactor(hierarchy): 移除 HierarchyUtil 相关组件,改为使用更精简的 HierarchyIterator 实现
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
package org.dromara.hutool.core.stream;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.dromara.hutool.core.collection.ListUtil;
|
||||
@@ -28,13 +29,39 @@ import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* @author VampireAchao
|
||||
*/
|
||||
public class EasyStreamTest {
|
||||
|
||||
|
||||
@Test
|
||||
void testIterateHierarchies() {
|
||||
// 创建一个三层的树结构,每个节点都有两个子节点
|
||||
Node node = new Node("1", Arrays.asList(
|
||||
new Node("1-1", Arrays.asList(
|
||||
new Node("1-1-1", null),
|
||||
new Node("1-1-2", null)
|
||||
)),
|
||||
new Node("1-2", Arrays.asList(
|
||||
new Node("1-2-1", null),
|
||||
new Node("1-2-2", null)
|
||||
))
|
||||
));
|
||||
|
||||
// 按广度度优先遍历树结构
|
||||
List<String> allNodes = new ArrayList<>();
|
||||
EasyStream.iterateHierarchies(node, node1 -> node1.children)
|
||||
.forEach(node1 -> allNodes.add(node1.id));
|
||||
Assertions.assertEquals(Arrays.asList("1", "1-1", "1-2", "1-1-1", "1-1-2", "1-2-1", "1-2-2"), allNodes);
|
||||
|
||||
// 按广度度优先遍历树结构,忽略id为1-1的节点与以其为根节点的子树
|
||||
List<String> filteredNodes = new ArrayList<>();
|
||||
EasyStream.iterateHierarchies(node, node1 -> node1.children, node1 -> !node1.id.equals("1-1"))
|
||||
.forEach(node1 -> filteredNodes.add(node1.id));
|
||||
Assertions.assertEquals(Arrays.asList("1", "1-2", "1-2-1", "1-2-2"), filteredNodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcat() {
|
||||
final Stream<Integer> stream1 = Stream.of(1, 2);
|
||||
@@ -568,4 +595,19 @@ public class EasyStreamTest {
|
||||
private String name;
|
||||
private BigDecimal count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点
|
||||
*/
|
||||
private static class Node {
|
||||
private final String id;
|
||||
private List<Node> children;
|
||||
private Node(String id, List<Node> children) {
|
||||
this.id = id;
|
||||
this.children = children;
|
||||
}
|
||||
public Node(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,12 +17,44 @@ import org.dromara.hutool.core.collection.set.SetUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class StreamUtilTest {
|
||||
|
||||
@Test
|
||||
void testIterateHierarchies() {
|
||||
// 创建一个三层的树结构,每个节点都有两个子节点
|
||||
Node node = new Node("1", Arrays.asList(
|
||||
new Node("1-1", Arrays.asList(
|
||||
new Node("1-1-1", null),
|
||||
new Node("1-1-2", null)
|
||||
)),
|
||||
new Node("1-2", Arrays.asList(
|
||||
new Node("1-2-1", null),
|
||||
new Node("1-2-2", null)
|
||||
))
|
||||
));
|
||||
|
||||
// 按广度度优先遍历树结构
|
||||
List<String> allNodes = new ArrayList<>();
|
||||
StreamUtil.iterateHierarchies(node, node1 -> node1.children)
|
||||
.forEach(node1 -> allNodes.add(node1.id));
|
||||
Assertions.assertEquals(Arrays.asList("1", "1-1", "1-2", "1-1-1", "1-1-2", "1-2-1", "1-2-2"), allNodes);
|
||||
|
||||
// 按广度度优先遍历树结构,忽略id为1-1的节点与以其为根节点的子树
|
||||
List<String> filteredNodes = new ArrayList<>();
|
||||
StreamUtil.iterateHierarchies(node, node1 -> node1.children, node1 -> !node1.id.equals("1-1"))
|
||||
.forEach(node1 -> filteredNodes.add(node1.id));
|
||||
Assertions.assertEquals(Arrays.asList("1", "1-2", "1-2-1", "1-2-2"), filteredNodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofTest(){
|
||||
final Stream<Integer> stream = StreamUtil.of(2, x -> x * 2, 4);
|
||||
@@ -61,4 +93,19 @@ public class StreamUtilTest {
|
||||
Assertions.assertEquals(0, stream.toArray().length);
|
||||
}
|
||||
// ================ stream test end ================
|
||||
|
||||
/**
|
||||
* 节点
|
||||
*/
|
||||
private static class Node {
|
||||
private final String id;
|
||||
private List<Node> children;
|
||||
private Node(String id, List<Node> children) {
|
||||
this.id = id;
|
||||
this.children = children;
|
||||
}
|
||||
public Node(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,126 @@
|
||||
package org.dromara.hutool.core.tree;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* test for {@link HierarchyIterator}
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
class HierarchyIteratorTest {
|
||||
|
||||
@Test
|
||||
void testNoSuchElementException() {
|
||||
Node node = createTree();
|
||||
// 根节点也被忽略,将会抛出异常
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> HierarchyIterator.depthFirst(node, node1 -> node1.children, node1 -> false));
|
||||
// 忽略所有节点,将会抛出NoSuchElementException
|
||||
HierarchyIterator<Node> iterator = HierarchyIterator.depthFirst(node, node1 -> node1.children, node1 -> node1.id.equals("1"));
|
||||
Assertions.assertSame(node, iterator.next());
|
||||
Assertions.assertThrows(NoSuchElementException.class, iterator::next);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDepthFirstWithFilter() {
|
||||
Node node = createTree();
|
||||
// 按深度度优先遍历树结构,忽略id为1-1的节点与以其为根节点的子树
|
||||
List<String> nodes = new ArrayList<>();
|
||||
HierarchyIterator<Node> iterator = HierarchyIterator.depthFirst(
|
||||
node, node1 -> node1.children, node1 -> !node1.id.equals("1-1")
|
||||
);
|
||||
while (iterator.hasNext()) {
|
||||
Node node1 = iterator.next();
|
||||
nodes.add(node1.id);
|
||||
}
|
||||
Assertions.assertEquals(Arrays.asList("1", "1-2", "1-2-1", "1-2-2"), nodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDepthFirst() {
|
||||
Node node = createTree();
|
||||
// 按深度度优先遍历树结构
|
||||
List<String> nodes = new ArrayList<>();
|
||||
HierarchyIterator<Node> iterator = HierarchyIterator.depthFirst(node, node1 -> node1.children);
|
||||
while (iterator.hasNext()) {
|
||||
Node node1 = iterator.next();
|
||||
nodes.add(node1.id);
|
||||
}
|
||||
Assertions.assertEquals(Arrays.asList("1", "1-1", "1-1-1", "1-1-2", "1-2", "1-2-1", "1-2-2"), nodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBreadthFirstWithFilter() {
|
||||
Node node = createTree();
|
||||
// 按深度度优先遍历树结构,忽略id为1-1的节点与以其为根节点的子树
|
||||
List<String> nodes = new ArrayList<>();
|
||||
HierarchyIterator<Node> iterator = HierarchyIterator.breadthFirst(
|
||||
node, node1 -> node1.children, node1 -> !node1.id.equals("1-1")
|
||||
);
|
||||
while (iterator.hasNext()) {
|
||||
Node node1 = iterator.next();
|
||||
nodes.add(node1.id);
|
||||
}
|
||||
Assertions.assertEquals(Arrays.asList("1", "1-2", "1-2-1", "1-2-2"), nodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBreadthFirst() {
|
||||
Node root = createGraph();
|
||||
// 按深度度优先遍历图结构
|
||||
List<String> nodes = new ArrayList<>();
|
||||
HierarchyIterator<Node> iterator = HierarchyIterator.breadthFirst(root, node -> node.children);
|
||||
while (iterator.hasNext()) {
|
||||
Node node = iterator.next();
|
||||
nodes.add(node.id);
|
||||
}
|
||||
Assertions.assertEquals(Arrays.asList("1", "4", "2", "3"), nodes);
|
||||
}
|
||||
|
||||
private static Node createGraph() {
|
||||
// 构建一个包含四个节点的图,每一个节点都有两个相邻节点
|
||||
Node node1 = new Node("1");
|
||||
Node node2 = new Node("2");
|
||||
Node node3 = new Node("3");
|
||||
Node node4 = new Node("4");
|
||||
node1.children = Arrays.asList(node4, node2);
|
||||
node2.children = Arrays.asList(node1, node3);
|
||||
node3.children = Arrays.asList(node2, node4);
|
||||
node4.children = Arrays.asList(node3, node1);
|
||||
return node1;
|
||||
}
|
||||
|
||||
private static Node createTree() {
|
||||
// 构建一个三层的树,每一个节点都有两个子节点
|
||||
return new Node("1", Arrays.asList(
|
||||
new Node("1-1", Arrays.asList(
|
||||
new Node("1-1-1", null),
|
||||
new Node("1-1-2", null)
|
||||
)),
|
||||
new Node("1-2", Arrays.asList(
|
||||
new Node("1-2-1", null),
|
||||
new Node("1-2-2", null)
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点
|
||||
*/
|
||||
private static class Node {
|
||||
private final String id;
|
||||
private List<Node> children;
|
||||
private Node(String id, List<Node> children) {
|
||||
this.id = id;
|
||||
this.children = children;
|
||||
}
|
||||
public Node(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* https://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.core.tree.hierarchy;
|
||||
|
||||
import lombok.Data;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* test for {@link HierarchyUtil}
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
class HierarchyUtilTest {
|
||||
|
||||
@Data
|
||||
static class Node {
|
||||
private String parent;
|
||||
private String value;
|
||||
private List<Node> children;
|
||||
|
||||
public Node(final String parent, final String value) {
|
||||
this.parent = parent;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Node root;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
// 根节点
|
||||
root = new Node(null, "0");
|
||||
|
||||
// 第二层
|
||||
final Node first1 = new Node(root.value, "0-1");
|
||||
final Node first2 = new Node(root.value, "0-2");
|
||||
final Node first3 = new Node(root.value, "0-3");
|
||||
root.setChildren(Arrays.asList(first1, first2, first3));
|
||||
|
||||
// 第三层
|
||||
final Node second11 = new Node(first1.value, "0-1-1");
|
||||
final Node second12 = new Node(first1.value, "0-1-2");
|
||||
final Node second13 = new Node(first1.value, "0-1-3");
|
||||
first1.setChildren(Arrays.asList(second11, second12, second13));
|
||||
|
||||
final Node second21 = new Node(first2.value, "0-2-1");
|
||||
final Node second22 = new Node(first2.value, "0-2-2");
|
||||
final Node second23 = new Node(first2.value, "0-2-3");
|
||||
first2.setChildren(Arrays.asList(second21, second22, second23));
|
||||
|
||||
final Node second31 = new Node(first3.value, "0-3-1");
|
||||
final Node second32 = new Node(first3.value, "0-3-2");
|
||||
final Node second33 = new Node(first3.value, "0-3-3");
|
||||
first3.setChildren(Arrays.asList(second31, second32, second33));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTraverseByBreadthFirst() {
|
||||
// // 按广度优先遍历所有节点
|
||||
final List<String> nodes = new ArrayList<>();
|
||||
HierarchyUtil.traverseByBreadthFirst(root, HierarchyIteratorUtil.scan(t -> {
|
||||
nodes.add(t.getValue());
|
||||
return t.getChildren();
|
||||
}));
|
||||
Assertions.assertEquals(13, nodes.size());
|
||||
Assertions.assertEquals(
|
||||
Arrays.asList("0", "0-1", "0-2", "0-3", "0-1-1", "0-1-2", "0-1-3", "0-2-1", "0-2-2", "0-2-3", "0-3-1", "0-3-2", "0-3-3"),
|
||||
nodes
|
||||
);
|
||||
|
||||
// 按广度优先寻找 0-2-3
|
||||
final String target = HierarchyUtil.traverseByBreadthFirst(root, HierarchyIteratorUtil.find(Node::getChildren,
|
||||
t -> Objects.equals(t.getValue(), "0-2-3") ? t.getValue() : null
|
||||
));
|
||||
Assertions.assertEquals("0-2-3", target);
|
||||
|
||||
// 按广度优先获取 0-2 的所有子节点
|
||||
final List<String> children = HierarchyUtil.traverseByBreadthFirst(root, HierarchyIteratorUtil.collect(Node::getChildren,
|
||||
t -> Objects.equals(t.getParent(), "0-2") ? t.getValue() : null
|
||||
));
|
||||
Assertions.assertEquals(3, children.size());
|
||||
Assertions.assertEquals(new ArrayList<>(Arrays.asList("0-2-1", "0-2-2", "0-2-3")), children);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTraverseByDepthFirst() {
|
||||
// 按深度优先遍历所有节点
|
||||
final Set<String> nodes = new LinkedHashSet<>();
|
||||
HierarchyUtil.traverseByDepthFirst(root, HierarchyIteratorUtil.scan(t -> {
|
||||
nodes.add(t.getValue());
|
||||
return t.getChildren();
|
||||
}));
|
||||
Assertions.assertEquals(13, nodes.size());
|
||||
Assertions.assertEquals(
|
||||
new LinkedHashSet<>(Arrays.asList("0", "0-1", "0-1-1", "0-1-2", "0-1-3", "0-2", "0-2-1", "0-2-2", "0-2-3", "0-3", "0-3-1", "0-3-2", "0-3-3")),
|
||||
nodes
|
||||
);
|
||||
|
||||
// 按深度优先寻找 0-2-3
|
||||
final String target = HierarchyUtil.traverseByDepthFirst(root, HierarchyIteratorUtil.find(Node::getChildren,
|
||||
t -> Objects.equals(t.getValue(), "0-2-3") ? t.getValue() : null
|
||||
));
|
||||
Assertions.assertEquals("0-2-3", target);
|
||||
|
||||
// 按深度优先获取 0-2 的所有子节点
|
||||
final List<String> children = HierarchyUtil.traverseByDepthFirst(root, HierarchyIteratorUtil.collect(Node::getChildren,
|
||||
t -> Objects.equals(t.getParent(), "0-2") ? t.getValue() : null
|
||||
));
|
||||
Assertions.assertEquals(3, children.size());
|
||||
Assertions.assertEquals(new ArrayList<>(Arrays.asList("0-2-1", "0-2-2", "0-2-3")), children);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user