• 查询list转化为tree的两种方式及排序


    方式一,数据库查询tree;

    MyBatis collection 集合

    MyBatis 是数据持久层框架,支持定制化 SQL、存储过程以及高级映射。尤其强大在于它的映射语句,比如高级映射中的 collection 集合。

    collection 集合,集合常用的两个场景是集合的嵌套查询、集合的嵌套结果。集合的嵌套结果就是查询结果对应嵌套子对象。这里就是利用 collection 集合嵌套查询树形节点。下面来一一实现。

    查询树形节点 Web 案例

    创建数据库表

    节点表:

    CREATE TABLE `node` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(32) NOT NULL,
      `parent_id` int(11) unsigned NOT NULL,
      PRIMARY KEY (`id`),
      KEY `parent_id` (`parent_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COMMENT='节点表'
    

    简单的节点父子关系设计,下面插入几条数据:

    INSERT INTO node (name, parent_id) VALUES ('一级节点A', 0);
    INSERT INTO node (name, parent_id) VALUES ('一级节点B', 0);
    INSERT INTO node (name, parent_id) VALUES ('一级节点C', 0);
    INSERT INTO node (name, parent_id) VALUES ('二级节点AA', 1);
    INSERT INTO node (name, parent_id) VALUES ('二级节点aa', 1);
    INSERT INTO node (name, parent_id) VALUES ('二级节点BB', 2);
    INSERT INTO node (name, parent_id) VALUES ('三级级节点AAA', 4);
    INSERT INTO node (name, parent_id) VALUES ('三级级节点aaa', 4);
    INSERT INTO node (name, parent_id) VALUES ('三级级节点BBB', 6);

    重要的还是看 collection 在 xml 的映射实现,NodeMapper.xml 代码如下:

    <mapper namespace="org.mybatis.dao.NodeDao">
    
      <resultMap id="BaseTreeResultMap" type="org.mybatis.domain.Node">
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <collection column="id" property="next" javaType="java.util.ArrayList"
                    ofType="org.mybatis.domain.Node" select="getNextNodeTree"/>
      </resultMap>
    
      <resultMap id="NextTreeResultMap" type="org.mybatis.domain.Node">
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <collection column="id" property="next" javaType="java.util.ArrayList"
                    ofType="org.mybatis.domain.Node" select="getNextNodeTree"/>
      </resultMap>
    
      <sql id="Base_Column_List">
            id, name
        </sql>
    
      <select id="getNextNodeTree" resultMap="NextTreeResultMap">
        SELECT
        <include refid="Base_Column_List"/>
        FROM node
        WHERE parent_id = #{id}
      </select>
    
      <select id="getNodeTree" resultMap="BaseTreeResultMap">
        SELECT
        <include refid="Base_Column_List"/>
        FROM node
        WHERE parent_id = 0
      </select>
    
    </mapper>
    

    在 dao 层,我们只调用 getNodeTree 方法,parent_id = 0 代表顶级节点。然后通过 collection 节点继续调用 getNextNodeTree 方法进行循环调用。

    <collection column="id" property="next" javaType="java.util.ArrayList"
                    ofType="org.mybatis.domain.Node" select="getNextNodeTree"/>
    

    以下是关键的知识点:

    • column 代表会拿父节点 id ,作为参数获取 next 对象
    • javaType 代表 next 对象是个列表,其实可以省略不写
    • ofType 用来区分 JavaBean 属性类型和集合包含的类型
    • select 是用来执行循环哪个 SQL

    工程代码地址:https://github.com/JeffLi1993/myabtis-learning-example
    工程演示后的结果如图所示:

     
     
     
    方式二,内存计算
       方式一查询数据库效率较低,方式二一次查询所有数据,任何用stream流来计算转化为树,建议采用

    普通list转树状list

    public static List<User> list2tree(List<User> list) {
    List<User> result = new ArrayList<>();
    Map<Object, User> hash = list.stream().collect(Collectors.toMap(u -> u.getId(), u -> u));
    for (User u : list) {
    User p = hash.get(u.getParent());
    if (p == null) {
    result.add(u);
    } else {
    if (p.getChildren() == null) {
    p.setChildren(new ArrayList<>());
    }
    p.getChildren().add(u);
    }
    }
    return result;
    }

    树状list转普通list

    public static List<User> tree2list(List<User> list) {
    List<User> result = new ArrayList<>();
    for (User u : list) {
    List<User> c = u.getChildren();
    result.add(u);
    if (!CollectionUtils.isEmpty(c)) {
    result.addAll(tree2list(c));
    u.setChildren(null);//
    }
    }
    return result;
    }

    java8 stream多字段排序

    使用java8新特性,下面先来点基础的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    List<类> list; 代表某集合
     
    //返回 对象集合以类属性一升序排序
     
    list.stream().sorted(Comparator.comparing(类::属性一));
     
    //返回 对象集合以类属性一降序排序 注意两种写法
     
    list.stream().sorted(Comparator.comparing(类::属性一).reversed());//先以属性一升序,结果进行属性一降序
     
    list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()));//以属性一降序
     
    //返回 对象集合以类属性一升序 属性二升序
     
    list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二));
     
    //返回 对象集合以类属性一降序 属性二升序 注意两种写法
     
    list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序
     
    list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二));//先以属性一降序,再进行属性二升序
     
    //返回 对象集合以类属性一降序 属性二降序 注意两种写法
     
    list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,升序结果进行属性一降序,再进行属性二降序
     
    list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一降序,再进行属性二降序
     
    //返回 对象集合以类属性一升序 属性二降序 注意两种写法
     
    list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二).reversed());//先以属性一升序,升序结果进行属性一降序,再进行属性二升序,结果进行属性一降序属性二降序
     
    list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,再进行属性二降序<br><br><br>

    通过以上例子我们可以发现

    1. Comparator.comparing(类::属性一).reversed();

    2. Comparator.comparing(类::属性一,Comparator.reverseOrder());

    两种排序是完全不一样的,一定要区分开来 1 是得到排序结果后再排序,2是直接进行排序,很多人会混淆导致理解出错,2更好理解,建议使用2

    3.注意排序后需要另外一个lis来接收List<xxx> list= Stream.sort(xxx).collect(toList());

    class testRun {
        public static void main(String[] args) {
            List<test> testList = new ArrayList<>();
            Date d = DateUtils.now();
            for (int i = 1; i <= 3; i++) {
                test t = new test(i, DateUtils.addDays(d, i));
                testList.add(t);
            }
            for (int i = 1; i <= 3; i++) {
                test t = new test(i, DateUtils.addMonths(d, i));
                testList.add(t);
            }
     
            testList.forEach(o -> {
                System.out.println(o.toString());
            });
            List<test> sort = testList.stream().sorted(Comparator.comparing(test::getState).thenComparing(test::getTime,Comparator.reverseOrder())).collect(toList());
            System.out.println("------------------------------------");
            sort.forEach(o -> {
                System.out.println(o.toString());
            });
     
     
        }
    }


    参考

    链接:https://www.jianshu.com/p/fa20d4bd8ad4

    链接:https://blog.csdn.net/qq_41991665/article/details/90484690

     
  • 相关阅读:
    React数据通信父传子和子传父的使用
    React类组件中事件绑定this指向的三种方式
    React中函数组件与类组件的两种使用
    React中生命周期的讲解
    React中Props的详细使用和props的校验
    vue3.2新增指令vmemo的使用
    Cypress测试报告生成
    逆双线性插值及其推导
    2022年6月股票推荐
    操作系统:设备I/O 如何表示设备类型与设备驱动?
  • 原文地址:https://www.cnblogs.com/yuluoxingkong/p/11912821.html
Copyright © 2020-2023  润新知