前两天有用到一个简单的树结构,和平常数据结构关注二叉树、BTree的观察点不同;这边主要是介绍一种树如何通过常见的树数据存储结构(各节点都包含parentId的数据)构造或操作。
Tree接口:
import java.util.List;
/**
* @author: huangpp02
* @time: 2018-02-26 11:18
*/
public interface Tree<T> {
/**
* 自身属性
*
* @return
*/
T getValue();
/**
* 子属性
*
* @return
*/
List<Tree<T>> getChildren();
/**
* 是否是叶子节点
* @return
*/
boolean isLeaf();
}
Tree的默认实现:DefaultTree
/**
* @author: huangpp02
* @time: 2018-02-26 11:24
*/
@Setter
public class DefaultTree<T> implements Tree<T> {
private T value;
private List<Tree<T>> children;
@Override
public T getValue() {
return value;
}
@Override
public List<Tree<T>> getChildren() {
return children;
}
@Override
public boolean isLeaf() {
return CollectionUtils.isEmpty(children);
}
}
重点来了,如何构造Tree?
public class TreeUtils {
/**
*
* @param initValue 树初始化节点
* @param childrenFunc 根据自身获取子节点列表
* @param <T> tree对应的value值
* @return 树
*/
public static <T> Tree<T> buildTree(T initValue, Function<T, List<T>> childrenFunc) {
DefaultTree<T> result = new DefaultTree<>();
result.setValue(initValue);
List<T> children = childrenFunc.apply(initValue);
if (!CollectionUtils.isEmpty(children)) {
result.setChildren(CollectionUtils.selectList(children, child -> buildTree(child, childrenFunc)));
}
return result;
}
}
上面是抽象了一个根据当前结点找到子节点列表的childrenFunc,这个Func要如何构造,可以参考下面的测试用例:
public class TestTreeUtils {
@Getter
@Setter
static class Area {
int parentId;
int id;
String name;
public Area(int parentId, int id, String name) {
this.parentId = parentId;
this.id = id;
this.name = name;
}
@Override
public String toString() {
return name;
}
}
private List<Area> areas;
@Before
public void init() {
areas = new ArrayList<>();
areas.add(new Area(0, 1, "湖北省"));
areas.add(new Area(0, 2, "福建省"));
areas.add(new Area(0, 3, "广东省"));
areas.add(new Area(1, 4, "武汉市"));
areas.add(new Area(1, 5, "潜江市"));
areas.add(new Area(4, 6, "武昌区"));
areas.add(new Area(2, 7, "泉州市"));
areas.add(new Area(2, 8, "厦门市"));
areas.add(new Area(8, 9, "思明区"));
areas.add(new Area(3, 10, "深圳市"));
areas.add(new Area(3, 11, "广州市"));
}
@Test
public void testBuild() {
Tree<Area> areaTree = TreeUtils.buildTree(new Area(0, 0, "中国"), new ChildFunc(areas));
TreeUtils.preOrder(areaTree).stream().forEach(System.out::println);
}
private class ChildFunc implements Function<Area, List<Area>> {
private Map<Integer, List<Area>> parentIdMap;
public ChildFunc(List<Area> areas) {
parentIdMap = CollectionUtils.toMapList(areas, Area::getParentId);
}
@Override
public List<Area> apply(Area area) {
return parentIdMap.get(area.getId());
}
}
}