树在很多地方都会用到,比如权限菜单、省市县等等,思路就是先把列表查出来,然后再生成一棵树
目录
数据初始化(必看)
方法一:使用递归生成树
方法二:非递归生成树
方法三:封装第二种方法(cv直接看这种)
初始化城市对象
public class City {
/**
* 城市id
*/
private Integer cityId;
/**
* 城市名称
*/
private String cityName;
/**
* 父id
*/
private Integer parentId;
/**
* 孩子们
*/
private List children;
/**
* 构造方法
*
* @param cityId 城市id
* @param cityName 城市名称
* @param parentId 父id
*/
public City(Integer cityId, String cityName, Integer parentId) {
this.cityId = cityId;
this.cityName = cityName;
this.parentId = parentId;
}
// 省略get、set
}
初始化数据
// 初始化城市信息
List cityList = new LinkedList<>() {{
add(new City(1, "福建省", 0));
add(new City(2, "浙江省", 0));
add(new City(3, "江苏省", 0));
add(new City(4, "厦门市", 1));
add(new City(5, "漳州市", 1));
add(new City(6, "泉州市", 1));
add(new City(7, "集美区", 4));
add(new City(8, "同安区", 4));
add(new City(9, "金华市", 2));
}};
// 要获取的节点id
// 在这里,0表示所有省份,1表示福建省下的所有市,以此类推
final Integer parentId = 0;
// 生成一棵树(getTree方法一会儿会实现)
List treeList = getTree(cityList, parentId);
/**
* 获取一棵树
*
* @param list 列表
* @param parentId 要获取的父节点id
* @return 一棵树
*/
public static List getTree(List list, Integer parentId) {
// 获取兄弟
List brotherList = list.stream().filter(city -> city.getParentId().equals(parentId)).collect(Collectors.toList());
// 获取上面这些兄弟的后代
List childrenList = list.stream().filter(city -> !city.getParentId().equals(parentId)).collect(Collectors.toList());
// 遍历兄弟
brotherList.forEach(brother -> {
// 遍历兄弟的后代
childrenList.forEach(child -> {
// 如果找到了兄弟的孩子
if (child.getParentId().equals(brother.getCityId())) {
// 如果children为空,就让它等于new LinkedList<>()
brother.setChildren(Optional.ofNullable(brother.getChildren()).orElse(new LinkedList<>()));
// 把兄弟的孩子添加进去
brother.getChildren().add(child);
}
});
});
// 遍历兄弟,递归获取兄弟的孩子
brotherList.forEach(brother -> getTree(childrenList, brother.getCityId()));
// 返回兄弟们
return brotherList;
}
相比递归,其实更推荐这种方法,这种方其实两次遍历list就好了,第一次遍历是根据parentId在map里面进行分组,第二次是根据id,去map找它的孩子,这样说可能还是有些人还是不太理解,代码写出来,打个断点研究一下就一目了然了
/**
* 获取一棵树
*
* @param list 列表
* @param parentId 要获取的父节点id
* @return 一棵树
*/
private static List getTree2(List list, Integer parentId) {
// 存放按parentId分组的数据
Map> map = new LinkedHashMap<>();
list.forEach(city -> {
// 取出同一个父亲下的兄弟
List brotherList = map.get(city.getParentId());
// 判断非空
brotherList = brotherList == null ? new LinkedList<>() : brotherList;
// 加入到兄弟里面
brotherList.add(city);
// 把兄弟们重新放到map里面
map.put(city.getParentId(), brotherList);
});
list.forEach(city -> {
// 去map找它孩子
List childrenList = map.get(city.getCityId());
// 把它的孩子丢进去
city.setChildren(childrenList);
});
return map.get(parentId);
}
可能有些人看到这些算法的代码会脑阔疼,这里把第二种方法封装起来,然后小改动一下就可以
新建一个类
public abstract class Tree {
/**
* id
*/
private Serializable id;
/**
* 父id
*/
private Serializable parentId;
/**
* 孩子们
*/
private List extends Tree> children = new ArrayList<>();
// 省略get、set
}
要生成的对象继承这个类
public class City extends Tree {
...
}
根据以下注释进行修改
public class City extends Tree {
// 如果有children字段,则把children字段去掉,会和父类的冲突
// private List children;
public City(Integer cityId, String cityName, Integer parentId) {
this.cityId = cityId;
this.cityName = cityName;
this.parentId = parentId;
// 给父类赋值上id
super.setId(cityId);
// 给父类赋上parentId
super.setParentId(this.parentId);
}
public void setCityId(Integer cityId) {
this.cityId = cityId;
// 给父类赋上id
super.setId(cityId);
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
// 给父类赋上parentId
super.setParentId(parentId);
}
...
}
获取一棵树,核心方法,直接cv
/**
* 获取一棵树
*
* @param list 列表
* @param parentId 要获取的父id
* @param 泛型,必须继承Tree
* @return 一棵树
*/
private static List getTree(List list, Serializable parentId) {
Map> map = new LinkedHashMap<>();
// 根据parentId进行分组
for (T t : list) {
// 取出同一个父亲下的兄弟
List list2 = map.get(t.getParentId());
// 判断非空
list2 = list2 == null ? new LinkedList<>() : list2;
// 加入到兄弟里面
list2.add(t);
// 把兄弟们重新放到map里面
map.put(t.getParentId(), list2);
}
for (T t : list) {
// 去map找它孩子
List list2 = map.get(t.getId());
// 把它孩子丢进去
t.setChildren(list2);
}
return map.get(parentId);
}
直接调用
List treeList = getTree(cityList, parentId);