原创

通用列表处理(以树结构为例子)

温馨提示:
本文最后更新于 2024年09月24日,已超过 213 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

最近接到一个需求是处理一个列表,写完之后给领导看了一下,给出的建议是这个方法虽然当前只在一处使用但是往后肯定会进行复用的(是传入不同的类型列表复用并不是单一类型复用),然后给了一个公司之前的项目中的方法作为参考,这个参考方法是一个构建树结构的方法只要满足条件那么这个列表就能构建成为一个树形结构。这里根据自己的思路自己写了一个简单的版本。

实现步骤如下:

1:先建立一个约束树形的父类,再建立两个继承这个类型的子类这里使用区域和数据字典来测试

2:编写通用方法

3:分别给区域和数据字典这两个类写入测试数据

4:对两个类分别进行测试

步骤一:先建立一个约束树形的父类,再建立两个继承这个类型的子类这里使用区域和数据字典来测试

1.先建立一个约束树形的父类,具体的代码如下:

@Data
public class Tree {
    private Integer id;
    private Integer parentId;
    private List<Object> children = new ArrayList<>();
    public void addChildren(Object o){
        children.add(o);
    }
}

这里如此设计是因为后续采用的是迭代的方式进行树结构处理而不是迭代的方式处理。

2.建立两个实体类,并且继承这个Tree类,分别是区域和数据字典

区域实体类具体代码如下:

@Data
public class TreeA extends Tree{
    private Integer id;
    private String province;
    private String city;
    private String region;
}

数据字典实体类具体代码如下:

@Data
public class TreeB extends Tree{
    private Integer id;
    private String title;
    private String code;
    private String value;
}

步骤二:编写一个处理树结构的通用方法,具体代码如下:

@Component
public class Traverse {

    public List<? extends Tree> traverseTree(List<? extends Tree> treeList){
        Map<Integer, Object> treeMap = new HashMap<>();
        //先遍历一遍所有的数据并将他们放入map中,key值为每条数据的Id
        treeList.forEach(item->{
            if (!treeMap.containsKey(item.getId())){
                treeMap.put(item.getId(), item);
            }
        });
        /**
         * 通过迭代的方式进行判断当前的数据是否为某一个父节点的子节点,如果是则通过parentId(这里的parentId指向父节点Id)
         * 获取父节点,然后使用父节点的children字段添加当前这条数据,最后再清除这条数据防止重复添加
         */
        Iterator<? extends Tree> iterator = treeList.iterator();
        while (iterator.hasNext()) {
            Tree next = iterator.next();
            Integer parentId = next.getParentId();
            if (parentId != null && treeMap.containsKey(parentId)) {
                Tree o = (Tree)treeMap.get(parentId);
                o.addChildren(next);
                iterator.remove();
            }
        }
        return treeList;
    }
}

这里接收的是一个泛型列表,这个列表中的元素必须是继承了Tree的子类,所以这里采用List<? extends Tree>来进行判断传入列表是否符合条件,先将每个节点都放入map中,然后再重新遍历一次集合,每条数据都会区判断他们的parentId是否在map中存在,如果有则说明当前节点是这个parentId获取到的结点的子节点,那么就会通过map获取这个父节点对象并将当前结点添加到父节点中,最后再删除当前节点避免重复添加。

步骤三:编写测试数据,这里分别为两个不同的树类型添加测试数据,具体代码如下:

@RestController
@RequestMapping("test")
public class TestController {

    @Autowired
    private Traverse traverse;
    @GetMapping("tree/{type}")
    public List getTree(@PathVariable String type){
        //这个是区域树的测试数据
        if ("0".equals(type)){
            List<TreeA> treeAS = new ArrayList<>();
            //添加省级单位
            TreeA treeA1 = new TreeA();
            treeA1.setId(0);
            treeA1.setCity("广东");
            treeA1.setProvince("广东");
            //添加市级单位
            TreeA treeA2 = new TreeA();
            treeA2.setId(2);
            treeA2.setCity("深圳");
            treeA2.setProvince("广东");
            treeA2.setParentId(0);
            TreeA treeA3 = new TreeA();
            //添加市级单位
            treeA3.setId(1);
            treeA3.setCity("广州");
            treeA3.setProvince("广东");
            treeA3.setParentId(0);
            //添加区级单位
            TreeA treeA4 = new TreeA();
            treeA4.setId(5);
            treeA4.setCity("广州");
            treeA4.setProvince("广东");
            treeA4.setRegion("天河区");
            treeA4.setParentId(1);
            //添加数据
            treeAS.add(treeA1);
            treeAS.add(treeA2);
            treeAS.add(treeA3);
            treeAS.add(treeA4);
            List<? extends Tree> trees = traverse.traverseTree(treeAS);
            return trees;
        }else{
            //这个是数据字典树的测试数据
            List<TreeB> treeBS = new ArrayList<>();
            TreeB treeB1 = new TreeB();
            treeB1.setId(0);
            treeB1.setTitle("01-性别");
            TreeB treeB2 = new TreeB();
            treeB2.setId(1);
            treeB2.setCode("0101");
            treeB2.setValue("男");
            treeB2.setTitle(treeB2.getCode() + "-" + treeB2.getValue());
            treeB2.setParentId(0);
            TreeB treeB3 = new TreeB();
            treeB3.setId(2);
            treeB3.setCode("0102");
            treeB3.setValue("女");
            treeB3.setTitle(treeB3.getCode() + "-" + treeB3.getValue());
            treeB3.setParentId(0);
            treeBS.add(treeB1);
            treeBS.add(treeB2);
            treeBS.add(treeB3);
            List<? extends Tree> trees = traverse.traverseTree(treeBS);
            return trees;
        }

    }
}

这里为了更直观的展示树结构所以采用接口来调用,通过type来区分获取哪种类型的树结构,type等于0时获取到的是区域类型的树结构而type不等于0时获取到的是数据字典类型的树结构(这里只用于测试)。

步骤4:编写玩测试数据之后,直接使用postman或则其他工具进行调用,具体结果如下:

区域树:

数据字典树:

可以看到,使用同一个处理方法传入不同的对象也可以处理树形结构,当然该方法还有很多缺陷是我没想到的也没有办法和公司架构师写的方法相比,这里仅是记录一下思路。

正文到此结束