V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ccppgo
V2EX  ›  Java

Java 新手又来请教问题了, 把 mysql join 查询 改成单表查询然后自己组装, 求问如何优化我写的 stream 操作

  •  
  •   ccppgo · 144 天前 · 1308 次点击
    这是一个创建于 144 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下:

    WmHBuQ.png WmHwjg.png

    public class Service{
    
        public IPage<ChannelAgentOrgVO> getChannelAgentTeamStream(Page<ChannelAgentOrgVO> page, Channel channel) {
    
            Page<Channel> channelPage = this.getPage(new Page<>(page.getCurrent(), page.getSize()), channel);
    
            Stream<Channel> channelStream = channelPage.getRecords().stream();
            Stream<ChannelAgentOrgVO> voStream = channelStream.map(c -> {
                ChannelAgentOrgVO vo = new ChannelAgentOrgVO();
                vo.setChannel(c);
                return vo;
            });
            List<Long> channelOrgIds = channelPage.getRecords().stream().map(Channel::getChannelOrgId).collect(Collectors.toList());
            List<Long> agentOrgIds = channelPage.getRecords().stream().map(Channel::getAgentOrgId).collect(Collectors.toList());
            List<Long> agentTeamIds = channelPage.getRecords().stream().map(Channel::getAgentTeamId).collect(Collectors.toList());
    
    
            List<ChannelOrg> channelOrgList = channelOrgService.lambdaQuery().in(ChannelOrg::getId, channelOrgIds)
                    .select(ChannelOrg::getId,
                            ChannelOrg::getCode,
                            ChannelOrg::getName,
                            ChannelOrg::getFullName,
                            ChannelOrg::getType)
                    .list();
            List<Org> agentOrgList = agentOrgService.lambdaQuery().in(Org::getId, agentOrgIds)
                    .select(Org::getId,
                            Org::getCode,
                            Org::getName,
                            Org::getFullName,
                            Org::getType)
                    .list();
            List<AgentTeam> agentTeamList = agentTeamService.lambdaQuery().in(AgentTeam::getId, agentTeamIds)
                    .select(AgentTeam::getId,
                            AgentTeam::getName,
                            AgentTeam::getFullName,
                            AgentTeam::getAddressDetail,
                            AgentTeam::getAddressName,
                            AgentTeam::getAddressContact)
                    .list();
    
    
            voStream = voStream.peek(vo -> channelOrgList.forEach(channelOrg -> {
                if (Objects.equals(channelOrg.getId(), vo.getChannel().getChannelOrgId())) {
                    vo.setChannelOrg(channelOrg);
                }
            }));
            voStream = voStream.peek(vo -> agentOrgList.forEach(agentOrg -> {
                if (Objects.equals(agentOrg.getId(), vo.getChannel().getAgentOrgId())) {
                    vo.setAgentOrg(agentOrg);
                }
            }));
            voStream = voStream.map(vo -> {
                agentTeamList.forEach(agentTeam -> {
                    if (Objects.equals(agentTeam.getId(), vo.getChannel().getAgentTeamId())) {
                        vo.setAgentTeam(agentTeam);
                    }
                });
                return vo;
            });
    
    
            return new PageResult<>(channelPage, voStream.collect(Collectors.toList())).getVoPage();
        }
    }
    
    19 条回复    2021-07-17 11:31:10 +08:00
    shanghai1943
        1
    shanghai1943  
       144 天前
    为什么不把 xxOrgList 转成 Map<Long, Object> 的形式来组装数据?
    ccppgo
        2
    ccppgo  
    OP
       144 天前
    @shanghai1943 我很想这么做.. 但是我好像有点不开窍, 大佬能直接改我的代码让我学习下么.. 主楼已经把整个方法的代码贴出来了
    xuanbg
        3
    xuanbg  
       144 天前
    你自己组装的过程还能怎么优化?只能在查询上面进行优化。
    oneisall8955
        4
    oneisall8955  
       144 天前
    说实话,这里的 stream 没必要,为了用 sream 而用 stream,没有解决最核心的耗时性能问题

    channel,agent,agentTeam 各个 ID 集合,只需要 for 循环一次 channelPage.getRecords()就可以得到而代码用了三次 channelPage.getRecords().stream().map(Channel::getXxxId)

    voStream 三次 peek/map 也没必要,只需要一次 for 循环一次就可以 setChannelOrg/setAgentOrg/setAgentTeam
    并且 setChannelOrg/setAgentOrg/setAgentTeam,可以先将 hannelOrgList 、agentOrgList 、agentTeamList 先转三个 map,而不是形如 cchannelOrgList.forEach 。。。if (Objects.equals(channelOrg.getId(), vo.getChannel().getChannelOrgId())。。。这种每次都遍历 list
    ccppgo
        5
    ccppgo  
    OP
       144 天前
    @xuanbg 其实我意思是想看看各位大佬 操作 java stream 的 api 是怎么写, 有没有优化空间, 查询上面应该没问题了, 只是简单的单表查询用后用 id 关联..
    ccppgo
        6
    ccppgo  
    OP
       144 天前
    @oneisall8955 是的, 这其实就是我的问题所在, 我想看看怎么写这种操作, 因为不是很熟悉 Java,刚写第二周
    mitsuizzz
        7
    mitsuizzz  
       144 天前
    可以先把 id 查出来,然后并行通过 id 去查附加信息,然后循环一次,把信息组装下
    shanghai1943
        8
    shanghai1943  
       144 天前
    Map<Long, ChannelOrg> map =channelOrgList.stream().collect(Collectors.toMap(ChannelOrg::geChannelOrgId,ChannelOrg->ChannelOrg));
    其他两个类似转化
    然后用一个 for 循环生成最终对象并且把这三个 map 根据 id 获取 value 然后 set 到最终对象里
    oneisall8955
        9
    oneisall8955  
       144 天前
    @ccppgo #6 list 转 map 很简单吧

    ```
    class Foo{
    int code;
    string name;
    }

    // list 通过 stream 转 map
    List<Foo> list = new ArrayList<>();
    Map<Integer, Foo> map = list.stream().collect(Collectors.toMap(Foo::getCode, Function.identity(), (f1, f2) -> f1));
    Foo foo = map.get(1);

    // 使用 Guava 库
    Map<Integer, Foo> map = Maps.uniqueIndex(list,Foo::getCode);
    ```
    ccppgo
        10
    ccppgo  
    OP
       144 天前
    @shanghai1943
    @mitsuizzz
    @oneisall8955

    三位大佬, 我好像有点开悟了, 请问还有优化空间吗
    [![WnF2wt.png]( https://z3.ax1x.com/2021/07/15/WnF2wt.png)]( https://imgtu.com/i/WnF2wt)
    shanghai1943
        11
    shanghai1943  
       144 天前
    你这 voStream 里 setXXX 是不是跟下面 for 循环里做的事情重复了?
    ccppgo
        12
    ccppgo  
    OP
       144 天前
    #10

    还有个问题, stream 和 foreach 都能做得时候, 使用 stream 好过 foreach 吗
    ccppgo
        13
    ccppgo  
    OP
       144 天前
    @shanghai1943 是的, 我是想知道 使用 stream 和 foreach 哪个更好
    shanghai1943
        14
    shanghai1943  
       144 天前
    一般比较更好的时候会参考性能,可读性,以及后续维护成本来看。站在这几个方面来看的话,你觉得哪个更好?
    ccppgo
        15
    ccppgo  
    OP
       144 天前
    @shanghai1943 我看过一些网上的分析, 貌似数据量少时,foreach 性能稍微高一丢丢, 数据量大时 stream 性能更高, 而且还可以用多核并行, 并且 stream 的可读性和维护性 比 foreach 要好, 我感觉比较喜欢 stream
    thetbw
        16
    thetbw  
       143 天前
    lambdaQuery 是用于哪个 orm 吗
    ccppgo
        17
    ccppgo  
    OP
       143 天前
    @thetbw mybatis plus 自带的
    running17
        18
    running17  
       142 天前
    每次在项目里看到这种 forEach 嵌套 forEach 去筛选取值的,都会忍不住去改成 Map 的形式,一次循环可以完成事情的,虽然小数据量下效率不会有很大的提升,但是感觉思路习惯还是养成的==
    ccppgo
        19
    ccppgo  
    OP
       142 天前
    @running17 我已经学会了哈哈哈,stream toMap 永远滴神
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1444 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 17:43 · PVG 01:43 · LAX 09:43 · JFK 12:43
    ♥ Do have faith in what you're doing.