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

2021-07-15 14:20:55 +08:00
 ccppgo

代码如下:

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();
    }
}
1868 次点击
所在节点    Java
19 条回复
shanghai1943
2021-07-15 14:27:28 +08:00
为什么不把 xxOrgList 转成 Map<Long, Object> 的形式来组装数据?
ccppgo
2021-07-15 14:29:13 +08:00
@shanghai1943 我很想这么做.. 但是我好像有点不开窍, 大佬能直接改我的代码让我学习下么.. 主楼已经把整个方法的代码贴出来了
xuanbg
2021-07-15 14:31:46 +08:00
你自己组装的过程还能怎么优化?只能在查询上面进行优化。
oneisall8955
2021-07-15 14:48:31 +08:00
说实话,这里的 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
2021-07-15 14:50:28 +08:00
@xuanbg 其实我意思是想看看各位大佬 操作 java stream 的 api 是怎么写, 有没有优化空间, 查询上面应该没问题了, 只是简单的单表查询用后用 id 关联..
ccppgo
2021-07-15 14:51:24 +08:00
@oneisall8955 是的, 这其实就是我的问题所在, 我想看看怎么写这种操作, 因为不是很熟悉 Java,刚写第二周
mitsuizzz
2021-07-15 14:57:22 +08:00
可以先把 id 查出来,然后并行通过 id 去查附加信息,然后循环一次,把信息组装下
shanghai1943
2021-07-15 15:00:50 +08:00
Map<Long, ChannelOrg> map =channelOrgList.stream().collect(Collectors.toMap(ChannelOrg::geChannelOrgId,ChannelOrg->ChannelOrg));
其他两个类似转化
然后用一个 for 循环生成最终对象并且把这三个 map 根据 id 获取 value 然后 set 到最终对象里
oneisall8955
2021-07-15 15:10:15 +08:00
@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
2021-07-15 15:14:00 +08:00
@shanghai1943
@mitsuizzz
@oneisall8955

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

还有个问题, stream 和 foreach 都能做得时候, 使用 stream 好过 foreach 吗
ccppgo
2021-07-15 15:18:56 +08:00
@shanghai1943 是的, 我是想知道 使用 stream 和 foreach 哪个更好
shanghai1943
2021-07-15 15:21:27 +08:00
一般比较更好的时候会参考性能,可读性,以及后续维护成本来看。站在这几个方面来看的话,你觉得哪个更好?
ccppgo
2021-07-15 15:23:24 +08:00
@shanghai1943 我看过一些网上的分析, 貌似数据量少时,foreach 性能稍微高一丢丢, 数据量大时 stream 性能更高, 而且还可以用多核并行, 并且 stream 的可读性和维护性 比 foreach 要好, 我感觉比较喜欢 stream
thetbw
2021-07-16 17:22:42 +08:00
lambdaQuery 是用于哪个 orm 吗
ccppgo
2021-07-16 19:50:56 +08:00
@thetbw mybatis plus 自带的
running17
2021-07-17 11:10:30 +08:00
每次在项目里看到这种 forEach 嵌套 forEach 去筛选取值的,都会忍不住去改成 Map 的形式,一次循环可以完成事情的,虽然小数据量下效率不会有很大的提升,但是感觉思路习惯还是养成的==
ccppgo
2021-07-17 11:31:10 +08:00
@running17 我已经学会了哈哈哈,stream toMap 永远滴神

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/789702

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX