集合切片处理,看这篇就够了

729 阅读1分钟

前言

在日常开发中,集合分批次处理、按指定数量批量入库是常见操作,如何方便快捷的使用呢?

本文章介绍了四种解决方案,并最终完成最简单最快捷的策略。

如下代码,减少自己操作:

一、利用List循环赋值

  • 此方法非常笨拙,且速度很慢;不推荐使用

        List<HandoverExternalChat> externalUser = externalChat.list(queryWrapper);
        // 客户的external_userid列表,最多一次转移100个客户
        List<String> externalUserIds = new ArrayList<>();
        // 需要转群主的客户群ID列表。取值范围: 1 ~ 100
        List<String> chatIds = new ArrayList<>();
        // 初始化群分配
        List<ResignedTransferGroupResponse.FailedChat> list = new ArrayList<>();
        int a = 0;
        for (int i = 0; i < externalUser.size(); i++) {
            externalUserIds.add(externalUser.get(i).getExternalUserid());
            chatIds.add(externalUser.get(i).getChatId());
            a++;
            if (a == 99) {
                // 分配离职成员的客户
                request.setExternalUserid(externalUserIds);
                resignedTransferService.transferCustomer(request);
                // 分配离职成员的客户群
                transferGroupRequest.setChatIdList(chatIds);
                ResignedTransferGroupResponse result = resignedTransferService.groupChatTransfer(transferGroupRequest);
                list.addAll(result.getFailedChatList());
                a = 0;
                externalUserIds = new ArrayList<>();
                chatIds = new ArrayList<>();
            }
        }
        // 剩余不足100的进行再次处理
        if (a != 0) {
            // 分配离职成员的客户
            request.setExternalUserid(externalUserIds);
            resignedTransferService.transferCustomer(request);
            // 分配离职成员的客户群
            transferGroupRequest.setChatIdList(chatIds);
            ResignedTransferGroupResponse result = resignedTransferService.groupChatTransfer(transferGroupRequest);
            list.addAll(result.getFailedChatList());
        }
    

二、利用List截取

  • 此方法存在一定冗余操作,且速度慢;不推荐使用

        // 客户的userid列表,最多一次转移100个客户
        List<ExternalContactBehaviorData> list = new ArrayList<>();
        int a = 0;
        int b = 0;
        for (int i = 1; i < userList.size() + 1; i++) {
            b++;
            if (b == 99) {
                request.setUserId(ListUtil.sub(userList, a, i + a));
                ExternalContactBehaviorDataResponse userBehaviorData = externalContactService.getUserBehaviorData(request);
                if (!userBehaviorData.isOk()) {
                    throw new WecomCustomerException(userBehaviorData.getErrMsg());
                }
                list = userBehaviorData.getBehaviorData();
                a = i;
                b = 0;
            }
        }
        // 剩余不足100的进行再次处理
        if (b != 0) {
            request.setUserId(ListUtil.sub(userList, a, userList.size()));
            ExternalContactBehaviorDataResponse userBehaviorData = externalContactService.getUserBehaviorData(request);
            if (!userBehaviorData.isOk()) {
                throw new WecomCustomerException(userBehaviorData.getErrMsg());
            }
            list.addAll(userBehaviorData.getBehaviorData());
        }
    

三、利用CollUtil.sub方法

  • CollUtil.sub方法对集合切片,其他类型的集合会转换成List,封装List.subList方法,自动修正越界等问题,完全避免IndexOutOfBoundsException异常;推荐

        // 客户的external_userid列表,最多一次转移100个客户
        int size = batchVO.getChatId().size();
        int endIndex = 100;
        for (int i = 0; i < size; i += 100) {
            // 剩余不足100的进行再次处理
            if (i + 100 > size) {
                endIndex = size - i;
            }
            List<String> list = CollUtil.sub(batchVO.getChatId(), i, i + endIndex);
            request.setExternalUserid(list);
            resignedTransferService.transferCustomer(request);
        }
    

四、使用Lists.partition切分性能优化 

  • Lists.partition 是通过提供一层抽象来实现分片的,通过 get 返回一个子列表,子列表具体有 subList 来实现,速度快,代码简洁;非常推荐
  1. 依赖pom

        <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.1.1-jre</version>
        </dependency>
    

   2. 代码

// list 分片数量
List<List<MarketingResultBatch>> groupList = Lists.partition(updatePushResultList,100);
groupList.stream().forEach(list->{
    // 批量入库
    updateMarketingResultBatch1000(list,updatedTime);
});

   3. 注意事项

List<List<TreasureIntegrationVo>> resultPartition = Lists.partition(list, 500) 之后再对 list 进行 clear 操作,resultPartition也会被清空;

后来才发现谷歌它最终会调用 list.subList

subList 执行结果是获取 ArrayList 的一部分,返回的是 ArrayList 的部分视图;

对子集合的操作会反映到原集合, 对原集合的操作也会影响子集合。