Java获取一棵树的3种方法

树在很多地方都会用到,比如权限菜单、省市县等等,思路就是先把列表查出来,然后再生成一棵树

目录

数据初始化(必看)

方法一:使用递归生成树

方法二:非递归生成树

方法三:封装第二种方法(cv直接看这种)


数据初始化(必看)

初始化城市对象

public class City {
	/**
	 * 城市id
	 */
	private Integer cityId;

	/**
	 * 城市名称
	 */
	private String cityName;

	/**
	 * 父id
	 */
	private Integer parentId;

	/**
	 * 孩子们
	 */
	private List<City> 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<City> 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<City> treeList = getTree(cityList, parentId);

方法一:使用递归生成树

/**
 * 获取一棵树
 *
 * @param list 列表
 * @param parentId 要获取的父节点id
 * @return 一棵树
 */
public static List<City> getTree(List<City> list, Integer parentId) {
   // 获取兄弟
   List<City> brotherList = list.stream().filter(city -> city.getParentId().equals(parentId)).collect(Collectors.toList());
   // 获取上面这些兄弟的后代
   List<City> 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<City> getTree2(List<City> list, Integer parentId) {
   // 存放按parentId分组的数据
   Map<Integer, List<City>> map = new LinkedHashMap<>();

   list.forEach(city -> {
      // 取出同一个父亲下的兄弟
      List<City> 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<City> childrenList = map.get(city.getCityId());
      // 把它的孩子丢进去
      city.setChildren(childrenList);
   });
   return map.get(parentId);
}

方法三:封装第二种方法(cv直接看这种)

可能有些人看到这些算法的代码会脑阔疼,这里把第二种方法封装起来,然后小改动一下就可以

新建一个类

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<City> 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 <T> 泛型,必须继承Tree
 * @return 一棵树
 */
private static <T extends Tree> List<T> getTree(List<T> list, Serializable parentId) {
   Map<Serializable, List<T>> map = new LinkedHashMap<>();

   // 根据parentId进行分组
   for (T t : list) {
      // 取出同一个父亲下的兄弟
      List<T> 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<T> list2 = map.get(t.getId());
      // 把它孩子丢进去
      t.setChildren(list2);
   }
   return map.get(parentId);
}

 直接调用

List<City> treeList = getTree(cityList, parentId);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值