refactor(hierarchy): 移除 HierarchyUtil 相关组件,改为使用更精简的 HierarchyIterator 实现

This commit is contained in:
huangchengxing
2023-12-28 20:26:29 +08:00
parent 3f8ee8a429
commit 5e03f627b3
13 changed files with 567 additions and 590 deletions

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}