java中把一个list转tree的三种方法——工具类

java中把一个list转tree的三种实现方法

如何使用:

如果你的类中主键名称为id,父节点id名称为parentId,子节点列表名称为children,数据库中顶层父节点id值为“0”,可以直接调用只需传入需要转换list的方法。否则需要传入相应的字段名称,或者修改代码。

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author :Kite
 * @Date : 2023/5/8 9:52
 */
public class TreeUtil {

    /**
     * 通过递归方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByRecursion(List<T> list) {
        return buildTreeByRecursion(list, "id", "parentId", "children");
    }

    /**
     * 通过Map方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByMap(List<T> list) {
        return buildTreeByMap(list, "id", "parentId", "children", "0");
    }

    /**
     * 通过两层for循环方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByTwoLayersFor(List<T> list) {
        return buildTreeByTwoLayersFor(list, "id", "parentId", "children", "0");
    }

    /**
     * 通过递归方式构建树
     *
     * @param list         列表
     * @param idName       id名称
     * @param parentIdName 父id名称
     * @param childrenName 子节点列表名称
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByRecursion(List<T> list, String idName, String parentIdName, String childrenName) {
        if (StringUtils.isBlank(idName) || StringUtils.isBlank(parentIdName) || StringUtils.isBlank(childrenName)) {
            return new ArrayList<>();
        }
        List<T> returnList = new ArrayList<>();
        //获取list中所有的主键id
        List<String> ids = list.stream().map(o -> getFieldValue(o, idName).toString()).toList();

        for (T t : list) {
            String parentId = getFieldValue(t, parentIdName).toString();
            //如果是顶级节点, 遍历该父节点的所有子节点,因为顶层的parentId为0
            if (!ids.contains(parentId)) {
                buildTreeRecursive(list, t, idName, parentIdName, childrenName);
                returnList.add(t);
            }
        }
        if (returnList.isEmpty()) {
            returnList = list;
        }
        return returnList;
    }

    /**
     * 递归fn
     *
     * @param list 分类表
     * @param node 子节点
     */
    private static <T> void buildTreeRecursive(List<T> list, T node, String idName, String parentIdName, String childrenName) {
        // 得到子节点列表
        List<T> childList = getChildList(list, node, idName, parentIdName);
        setFieldValue(node, childList, childrenName);
        for (T child : childList) {
            if (hasChild(list, child, idName, parentIdName)) {
                buildTreeRecursive(list, child, idName, parentIdName, childrenName);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private static <T> List<T> getChildList(List<T> list, T node, String idName, String parentIdName) {
        List<T> tlist = new ArrayList<>();
        String oId = getFieldValue(node, idName).toString();
        for (T child : list) {
            String tParentId = getFieldValue(child, parentIdName).toString();
            if (tParentId.equals(oId)) {
                tlist.add(child);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private static <T> boolean hasChild(List<T> list, T t, String idName, String parentIdName) {
        return getChildList(list, t, idName, parentIdName).size() > 0;
    }

    /**
     * 通过Map方式构建树
     *
     * @param list           列表
     * @param idName         id名称
     * @param parentIdName   父id名称
     * @param childrenName   子节点列表名称
     * @param topParentIdVal 顶层节点父id的值
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByMap(List<T> list, String idName, String parentIdName, String childrenName, String topParentIdVal) {
        if (StringUtils.isBlank(idName) || StringUtils.isBlank(parentIdName) || StringUtils.isBlank(childrenName)) {
            return new ArrayList<>();
        }
        //根据parentId进行分组
        Map<String, List<T>> mapList = list.stream().collect(Collectors.groupingBy(o -> getFieldValue(o, parentIdName).toString()));
        //给每个节点设置子节点列表
        list.forEach(node -> setFieldValue(node, mapList.get(getFieldValue(node, idName).toString()), childrenName));
        return list.stream().filter(o -> topParentIdVal.equals(getFieldValue(o, parentIdName))).collect(Collectors.toList());
    }

    /**
     * 获取属性值
     *
     * @param o         对象
     * @param fieldName 属性名
     * @return {@link String}
     */
    private static Object getFieldValue(Object o, String fieldName) {
        try {
            Class<?> oClass = o.getClass();
            Field field = oClass.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(o);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置字段值
     *
     * @param o         对象
     * @param val       值
     * @param fieldName 属性名
     */
    private static void setFieldValue(Object o, Object val, String fieldName) {
        try {
            Class<?> oClass = o.getClass();
            Field field = oClass.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(o, val);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过两层for循环方式构建树
     *
     * @param list           列表
     * @param idName         id名称
     * @param parentIdName   父id名称
     * @param childrenName   子节点列表名称
     * @param topParentIdVal 顶层节点父id的值
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByTwoLayersFor(List<T> list, String idName, String parentIdName, String childrenName, String topParentIdVal) {
        List<T> resultList = new ArrayList<>();
        for (T node : list) {
            //如果是顶层节点
            if (topParentIdVal.equals(getFieldValue(node, parentIdName))) {
                resultList.add(node);
            }
            for (T child : list) {
                if (getFieldValue(child, parentIdName).equals(getFieldValue(node, idName))) {
                    List<T> childrenList = (List<T>) getFieldValue(node, childrenName);
                    if (CollectionUtils.isEmpty(childrenList)) {
                        childrenList = new ArrayList<>();
                        setFieldValue(node, childrenList, childrenName);
                    }
                    childrenList.add(child);
                }
            }
        }
        return resultList;
    }

}

三种方式对比

前两种方法的时间复杂度都和叶子节点的个数相关,我们假设叶子节点个数为m

方式一: 用递归的方法,时间复杂度等于:O(n +(n-m)* n),根据初始算法那篇文章的计算时间复杂度的方法,可以得到最终时间复杂度是O(n2)

方式二: 用两层嵌套循环的方法,时间复杂度等于:O(n +(n-m)* n),和方法一的时间复杂度是一样的,最终时间复杂度是O(n2)

方式三: 用两次遍历的方法,时间复杂度等于:O(3n),根据初始算法那篇文章的计算时间复杂度的方法,可以得到最终时间复杂度是O(n),但它的空间复杂度比前两种方法稍微大了一点,但是也是线性阶的,所以影响不是特别大。所以第三种方法是个人觉得比较优的一种方法

方式时间复杂度
递归平方阶,O(n2)
两层for循环平方阶,O(n2)
Map线性阶,O(n)
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 可以使用Java 8的Stream API来实现listmap的方法,示例代码如下: ``` import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class ListToMapDemo { public static void main(String[] args) { List<Person> personList = List.of( new Person("Tom", 20), new Person("Jerry", 25), new Person("Alice", 30) ); Map<String, Integer> personMap = personList.stream() .collect(Collectors.toMap(Person::getName, Person::getAge)); System.out.println(personMap); } static class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } } ``` 以上代码,我们定义了一个Person类,包含name和age两个属性。然后我们创建了一个Person列表,使用Stream API的collect方法将列表换为Map,其name作为key,age作为value。最后输出换后的Map。 ### 回答2: 在Java,可以通过以下方法List换为Map: 1. 创建一个空的Map对象,用于存储List的元素。 2. 使用增强的for循环遍历List的元素。 3. 对于每个元素,取出作为键的属性值和作为值的属性值。 4. 将键值对存入Map。 下面是一个示例代码: ```java import java.util.*; public class ListToMapExample { public static void main(String[] args) { List<Student> studentList = new ArrayList<>(); studentList.add(new Student(1, "Alice")); studentList.add(new Student(2, "Bob")); studentList.add(new Student(3, "Charlie")); Map<Integer, String> studentMap = listToMap(studentList); System.out.println(studentMap); // 输出:{1=Alice, 2=Bob, 3=Charlie} } public static Map<Integer, String> listToMap(List<Student> studentList) { Map<Integer, String> studentMap = new HashMap<>(); for(Student student : studentList) { studentMap.put(student.getId(), student.getName()); } return studentMap; } } class Student { private int id; private String name; public Student(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public String getName() { return name; } } ``` 在该示例,定义了一个Student类作为List的元素,Student类包含id和name属性。listToMap方法将Student列表换为具有id作为键、name作为值的Map对象。在主方法,将一个包含三个Student对象的List传递给listToMap方法,然后打印结果。 ### 回答3: 在Java,可以使用以下方法一个List换为Map: public static Map<Integer, String> convertListToMap(List<String> list) { Map<Integer, String> map = new HashMap<>(); for (int i = 0; i < list.size(); i++) { map.put(i, list.get(i)); } return map; } 以上方法的参数是一个List<String>,返回值是一个Map<Integer, String>。在方法,我们创建一个空的HashMap作为目标Map对象。接下来,我们使用循环遍历List,并将每个元素添加到Map。我们使用循环变量i作为Map的键,使用list.get(i)获取List对应位置的元素作为Map的值。最后,我们将生成的Map返回。 这个方法的作用是将List的元素按照索引顺序放入Map,以便可以通过索引快速访问List的元素。需要注意的是,List的每个元素都会被放入Map,并且Map的键是整型的索引,从0开始递增。 尽管这个方法可以在实际开发使用,但需要注意的是,由于同一个索引有可能对应多个元素,所以在换过程可能会出现冲突。为了避免这种情况,建议确保List的索引是唯一的。 希望以上回答对您有帮助,如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值