当前位置: 首页 > 工具软件 > Peek.js > 使用案例 >

stream中peek方法使用 和 peek转换树结构,树结构的各种实现

秦雅逸
2023-12-01

1. peek基本了解

会改变对象里的值

        Arrays.asList(new Person("1", "hah"), new Person("1", "hh"))
                .stream().peek(p -> p.setId("222")).forEach(System.out::println);



    @AllArgsConstructor
    @Data
    public static class Person {
        private String id;
        private String name;
    }
TestMain.Person(id=222, name=hah)
TestMain.Person(id=222, name=hh)

能分别打印 每一步的每个对象的操作

        List<Integer> list = Arrays.asList(4, 7, 9, 11, 12);

        list.stream().peek(x -> System.out.println("基本打印:" + x))
            	//每个元素+2,在用 分别打印。变成了:6 9 11 13 14
                .map(x -> x + 2).peek(x -> System.out.println("map:" + x))
            
            	//9 11 13 ,是奇数,才打印
                .filter(x -> x % 2 != 0).peek(x -> System.out.println("filter:" + x))
            
           		//只取 前 2个,那就是 9 和 11 了。
                .limit(2).peek(x -> System.out.println("limit:" + x))
            
                .collect(Collectors.toList()); //只有终止的时候,才执行peek
基本打印:4
map:6

基本打印:7
map:9
filter:9
limit:9

基本打印:9
map:11
filter:11
limit:11

//只取 前2个,打印 9 和 11
直接limit2打印:

基本打印:4
limit:4
基本打印:7
limit:7
        list.stream().peek(x -> System.out.println("基本打印:" + x))
                .filter(x -> x % 2 != 0).peek(x -> System.out.println("filter:" + x))
                .collect(Collectors.toList());

基本打印:4
    
基本打印:7
filter:7
    
基本打印:9
filter:9
    
基本打印:11
filter:11
    
基本打印:12
   
//基本打印了 5个数。 过滤打印了:7 9 11三个数。 
    
//在limit2,是在 7 9 11种 取2个的。
基本打印:4
    
基本打印:7
filter:7
limit:7
    
基本打印:9
filter:9
limit:9

2. java 8 Stream中操作类型和peek的使用

简介中间操作和终止操作peek结论

java 8 Stream中操作类型和peek的使用

简介

java 8 stream作为流式操作有两种操作类型,中间操作和终止操作。这两种有什么区别呢?

我们看一个peek的例子:

Stream<String> stream = Stream.of("one", "two", "three","four");

        stream.peek(System.out::println);

上面的例子中,我们的本意是打印出Stream的值,但实际上没有任何输出。

为什么呢?

中间操作和终止操作

一个java 8的stream是由三部分组成的。

  • 数据源,
  • 零个或一个或多个中间操作,
  • 一个或零个终止操作。

中间操作是对数据的加工,

  • 注意,中间操作是lazy操作,并不会立马启动,需要等待终止操作才会执行。

终止操作是stream的启动操作,

  • 只有加上终止操作,stream才会真正的开始执行。

所以,问题解决了,peek是一个中间操作,所以上面的例子没有任何输出。

3. peek

debug用途

我们看下peek的文档说明:peek主要被用在debug用途。

我们看下debug用途的使用:

Stream.of("one", "two", "three","four")
    
    .filter(e -> e.length() > 3).peek(e -> System.out.println("Filtered value: " + e))
    
    .map(String::toUpperCase).peek(e -> System.out.println("Mapped value: " + e))
    
                .collect(Collectors.toList());

上面的例子输出:

Filtered value: three
Mapped value: THREE

Filtered value: four
Mapped value: FOUR

上面的例子我们输出了stream的中间值,方便我们的调试。

为什么只作为debug使用呢?我们再看一个例子:

基本类型 不操作

Stream.of("one", "two", "three","four").peek(u -> u.toUpperCase())
                .forEach(System.out::println);

上面的例子我们使用peek将element转换成为upper case。然后输出:

one
two
three
four

可以看到stream中的元素并没有被转换成大写格式。

再看一个map的对比:

Stream.of("one", "two", "three","four").map(u -> u.toUpperCase())
                .forEach(System.out::println);

输出:

ONE
TWO
THREE
FOUR

可以看到map是真正的对元素进行了转换。

对象会改变值

当然peek也有例外,假如我们Stream里面是一个对象会怎么样?

    @Data
    @AllArgsConstructor
    static class User{
        private String name;
    }
        List<User> userList=Stream.of(new User("a"),new User("b"),new User("c"))
            .peek(u->u.setName("kkk"))
            .collect(Collectors.toList());

        log.info("{}",userList);

输出结果:

10:25:59.784 [main] INFO com.flydean.PeekUsage - [PeekUsage.User(name=kkk), PeekUsage.User(name=kkk), PeekUsage.User(name=kkk)]

我们看到如果是对象的话,实际的结果会被改变。

peek 和 map

为什么peek和map有这样的区别呢?

我们看下peek和map的定义:

Stream<T> peek(Consumer<? super T> action)
    
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

peek接收一个Consumer,而map接收一个Function。

Consumer是没有返回值的,

  • 它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。

而Function是有返回值的,

  • 这意味着对于Stream的元素的所有操作都会作为新的结果返回到Stream中。

这就是为什么peek String不会发生变化而peek Object会发送变化的原因。

peek方法 组成树结构:

        // 转换为树结构
        List<ResourceOutModel> resourceTree = 
            resourceList.stream()
                .filter(i -> i.getPid() == null) //过滤不符合的数据
            	//逐个元素设置 子节点,子节点的 子节点,递归
                .peek(i -> i.setChildren(getChildren(i, resourceList)))
            
                .collect(Collectors.toList());
    /**
     * 递归获取子节点
     *
     * @param node 节点对象
     * @param list 列表对象
     * @return 树对象
     */
    private List<ResourceOutModel> getChildren(ResourceOutModel node, List<ResourceOutModel> list) {
        return list.stream()
            	//当 参数的节点 == 循环的父节点(说明参数的节点 找到儿子了)
                .filter(i -> node.getId().equals(i.getPid()))
            	//再次递归设置调用
                .peek(i -> i.setChildren(getChildren(i, list)))
            
                .collect(Collectors.toList());
    }
public class ResourceOutModel {
    private Long id;
    private Long pid;


    private List<ResourceOutModel> children;
    
    }

*Java*中将*List转为树*形*结构*的三种方法:https://www.csdn.net/tags/MtzaAgwsMzkzMDAtYmxvZwO0O0OO0O0O.html

  • Mapper文件

  • 常规写法

  • Stream流处理

  • 使用SQL

    • oracle 使用 start with connect by prior
      
    • mysql使用 普通sql 或 自定义函数
      核心都是:find_in_set 和 concat 函数 
      

另一种树查询是:

    <!--自定义ResultMap -->
    <resultMap id="OrganizationListResultMap" type="com.car2p.accountsystem.model.b.orga.OrganizationOutModel">
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="busi_id" jdbcType="BIGINT" property="busiId"/>
        <result column="super_orga" jdbcType="BIGINT" property="superOrga"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="status" jdbcType="TINYINT" property="status"/>
        <result column="level" jdbcType="TINYINT" property="level"/>
    </resultMap>

    <!--树形查询-->
    <select id="getOrganizationList" resultMap="OrganizationListResultMap"
            parameterType="com.car2p.accountsystem.model.b.orga.OrganizationInModel">
        <!-- 最外层在套查询,筛选条件-->
        SELECT * FROM (

        select id,create_time,name,status,level,busi_id,super_orga from (
        select t1.id,t1.create_time,t1.name,t1.status,t1.level,t1.busi_id,t1.super_orga,
        <!-- 如果父ID在pids中查询,查到就执行拼接 或者 用ID在pids中查询,查不到就ischild赋值为0。最终结果赋值为ischild字段。
         如果parent_id 在@pid中,则将@pid 里面再加上parent_id,按行依次执行 -->
        if(find_in_set(super_orga, @pids) > 0, @pids := CONCAT(@pids,',', id), 0) as ischild

        from (
        <!-- 最内层是 条件查询,名字是模糊查询,创建时间 时间段查询。最后排序,现在是按照了 父类和ID -->select
        id,create_time,name,status,level,busi_id,super_orga from orga t
        where t.delete_flag = -1
        order by super_orga, id
        ) t1,
        <!-- @pids 初值赋值 -->        (select @pids := #{organizationId,jdbcType=BIGINT} ) t2
        ) t3
        where ischild != 0
        <!-- 1查自己和子组织  2,只查子组织(不需要下面这个或者情况)-->
        <if test="onlyChildren == 1">
            or id = #{organizationId,jdbcType=BIGINT}
        </if>
        ) ta
        where 1=1
        <if test="( organizationName != null)and( organizationName != '')">
            AND ta.name LIKE CONCAT(CONCAT('%', #{organizationName,jdbcType=VARCHAR}),'%')
        </if>
        <if test="( createTimeStart != null)and( createTimeEnd != null)">
            AND (ta.create_time between #{createTimeStart,jdbcType=TIMESTAMP} and #{createTimeEnd,jdbcType=TIMESTAMP})
        </if>
    </select>



        <!-- 保存的说明:
            or IF(id=@pids,1,0)
            AS ischild  加入这样的代码也行,为什么行,当Id= pids (第一次),就赋值为1,否则就赋值为0 ,
            肯定每次执行的第一次,就赋值为1了。

            下面的 这个SQL的说明:假如全表数据是:
            id  pid
            1	0
            2	1
            3	2
            4	6
            我们想输入的查询是1,(加上上面这个代码才执行1),现在是第一次为true是 2的 父ID是1,刚好,
            FIND_IN_SET(org_parent_id为1, @pids为1)
            执行拼接。 1,2 。此时pids变成 1,2
            @pids := CONCAT(@pids, ',', id)
            下次 ID,变成3, id 3的 父类是 2, 2 在PID 1,2 中
            下下次 pids变成 1,2,3,但是后面的数据 没有符合的,符合的只有ID  2,3 (想要1 可以用or IF(id=@pids,1,0) 或者 下面的sql )

            树查询解释,从新开始

            1,oracle 使用 start with connect by prior
            下递归,包含自己:
            select * from dept start with id='1001' connet by prior id=pid;
            如改成上递归,只需要 prior后面互换:pid=id;
            如向下递归,不包含自己只需 把id改成pid

            2,mysql使用 普通sql 或 自对应函数

            一,sql查1下的所有子节点
            select id from (
                          select t1.id,
                          if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids,',', id), 0) as ischild
                          from (
                               select id,parent_id from menu t where t.status = 1 order by parent_id, id
                              ) t1,
                              (select @pids := 1) t2
                         ) t3 where ischild != 0
             如果包含子节点,sql后加入 or id=1

            1,t2是@pids赋值,
            2,t1是全表查,根据 父id和子id排序(目的是 使有序的循环)
            如:t1 前的sql 会执行出这样的结果。第一列是id,第二类父id
            1	0
            11	1
            12	1
            111	11
            3,find_in_set(parent_id, @pids) > 0 ,第一次执行 pids=1,id为1的 父id为0,0不再1中,执行为 假。
            第二次 pids =1,id为 11的 父id为1,1在1中,执行为真。
            4,使用 id为 11,执行下列,变成:
            if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids,',', id), 0)
            pids= 有原来的1,拼接成 1,11 (这次执行的id)
            5, 执行到 id= 12,12的 父id为1,依然拼接到后面, ischild 的结果为
            1	0
            11	1,11
            12	1,11,12
            111	1,11,12,111
            6,此时判断 ischild != 0 即可得到 子节点。

            二,使用函数

            CREATE FUNCTION `GET_CHILD_NODE`(rootId varchar(100))
            RETURNS varchar(2000)

            BEGIN
            DECLARE str varchar(2000);
            DECLARE cid varchar(100);

            SET str = '$';
            SET cid = rootId;
            WHILE cid is not null DO
                SET str = concat(str, ',', cid);
                SELECT group_concat(id) INTO cid FROM tree where FIND_IN_SET(pid, cid);
            END WHILE;

            RETURN str;
            END

            使用:select * from tree where FIND_IN_SET(id, GET_CHILD_NODE(2))
            总体思路为:GET_CHILD_NODE 函数,得到 当前和 所有子节点
            在 FIND_IN_SET(id, 当前 和 所有子节点),所有需要的数据即取出来。
            函数说明:
            1,str = '$' 初值,无意义, 最终返回的是 str
            2,cid 先赋值 参数传递的,如例子是2
            3,进入循环 str = concat(str, ',', cid); ,str先把 cid 的2 给拼接上
            4,执行 SELECT group_concat(id) FROM tree where FIND_IN_SET(pid, '2')
            得到:所有 父id是2 的数据,取id,写入 INTO cid
            5,只要得到的不是null,循环继续,如此时得到的是3,cid为3
            6,在执行str 拼接,str已经是 $,2 拼接3 为 $,2,3
            7,此时在执行 SELECT FIND_IN_SET(pid, '3'),如4的pid是4, 4会筛序出来。
            8,str会继续拼接4,$,2,3,4 最终返回。
            9,使用这个函数 FIND_IN_SET(id,'$,2,3,4') ,id是2,3,4 的都会查出来,2是自己输入的参数。
        -->

使用 mapper

 <resultMap id="nestedCategory" type="com.baizhi.mybatisplus.entity.vo.ProductCategoryVo">
        <id property="id" column="id"/>
        <result property="title" column="title"/>
        <result property="sort" column="sort"/>
        <collection property="children"
                    ofType="com.baizhi.mybatisplus.entity.vo.ProductCategoryVo"
                    select="selectNestedListByParentId"
                    column="id" />
  </resultMap>
  <select id="selectNestedListByParentId" resultMap="nestedCategory">
        select id,sort,title from tbl_product_category where parent_id = ${parent_id}
  </select>

父子节点列表转成树形结构

https://blog.csdn.net/qq_31564573/article/details/117868011

 类似资料: