树在很多地方都会用到,比如权限菜单、省市县等等,思路就是先把列表查出来,然后再生成一棵树
目录
数据初始化(必看)
方法一:使用递归生成树
方法二:非递归生成树
方法三:封装第二种方法(cv直接看这种)
数据初始化(必看)
初始化城市对象
public class City {
private Integer cityId;
private String cityName;
private Integer parentId;
private List children;
public City(Integer cityId, String cityName, Integer parentId) {
this.cityId = cityId;
this.cityName = cityName;
this.parentId = parentId;
}
// 省略get、set
}
初始化数据
// 初始化城市信息 ListcityList = 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);
方法一:使用递归生成树
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找它的孩子,这样说可能还是有些人还是不太理解,代码写出来,打个断点研究一下就一目了然了
private static ListgetTree2(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); }
方法三:封装第二种方法(cv直接看这种)
可能有些人看到这些算法的代码会脑阔疼,这里把第二种方法封装起来,然后小改动一下就可以
新建一个类
public abstract class Tree {
private Serializable 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
private staticList 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); }
直接调用
ListtreeList = getTree(cityList, parentId);



