subList的缺陷

本文详细解析了 Java 中 ArrayList 的 subList 方法的工作原理,包括其返回的子列表如何与原始列表共享数据,以及如何通过 fail-fast 机制防止并发修改异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自:http://blog.csdn.net/chenssy/article/details/44102915

ArrayList的subList返回仅仅只是一个视图:

    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }
    private class SubList extends AbstractList<E> implements RandomAccess {
        private final AbstractList<E> parent;
        private final int parentOffset;
        private final int offset;
        int size;

        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }

        public E set(int index, E e) {
            rangeCheck(index);
            checkForComodification();
            E oldValue = ArrayList.this.elementData(offset + index);
            ArrayList.this.elementData[offset + index] = e;
            return oldValue;
        }

       ……

subListRangeCheck方式是判断fromIndex、toIndex是否合法,如果合法就直接返回一个SubList对象,注意在产生该new该对象的时候传递了一个参数this,它代表着原始list。
该SubLsit是ArrayList的内部类,它与ArrayList一样,都是继承AbstractList和实现RandomAccess接口。同时也提供了get、set、add、remove等list常用的方法。
从它的set方法可以看出,对SubList的修改实质上是修改了原始list。

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
List<String> subList = list.subList(1, 3);
System.out.println(list); // [a, b, c]
System.out.println(subList); // [b, c]

subList.add("d");
System.out.println(list); // [a, b, c, d]
System.out.println(subList); //[b, c, d]

subList.set(2, "e");
System.out.println(list); // [a, b, c, e]
System.out.println(subList); // [b, c, e]

subList生成子列表后,不要试图去操作原列表:

System.out.println(list.size());
System.out.println(subList.size());
list.add("f");
System.out.println(list.size());
System.out.println(subList.size());

在sublist.size()调用时会抛出java.util.ConcurrentModificationException,这是因为:

        public int size() {
            checkForComodification();
            return this.size;
        }
        private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }

这是由于fail-fast机制,对原列表有改动时,原列表的modCount会有变化,但是不会反映给子列表的modCount,而修改子列表时,实际上是修改了原列表,但是原列表的modCount修改后会重新复制给子列表的modCount。因此操作子列表没问题,但是操作原列表后,在使用子列表会抛出该异常。

推荐使用subList处理局部列表:比如有一个列表存在1000条记录,需要删除100-200位置处的数据:

list1.subList(100, 200).clear();
public void batchSendMsg(MultipartFile file, BatchSendMsgDTO dto) { final int BATCH_SIZE = 200; String accountId = dto.getAccountId(); List<String> idList = dto.getUserIds().stream().map(Object::toString).toList(); String fileName = FilenameUtils.getExtension(file.getOriginalFilename()); //如果是视频类型 需进行处理返回可访问地址 if (dto.getType().equals(TencentCloudImConstant.TIM_VIDEOFILE_ELEM)){ String localFilePath = uploadFolder + File.separator + fileName; log.info("{}->uploadFile method begin filename-> {}", "api", fileName); File savedFile = new File(localFilePath); String coverUrl; // 将上传的文件保存到本地临时文件夹中 try { file.transferTo(savedFile); File coverFile = generateVideoCover(file); //获取上传后的封面 coverUrl = tosHelperUtil.uploadCoverFile(coverFile.getPath(), dto.getClientId(), coverFile); dto.setCoverUrl(coverUrl); // 删除临时文件 FileUtil.del(coverFile); } catch (Exception e) { throw new CommonException("文件解析参数异常,请稍后重试"); } String absolutePath = savedFile.getAbsolutePath(); // 调用COS服务的uploadFile方法上传文件到COS UploadCosVO result = cosService.uploadFilePath(absolutePath, fileName, dto.getClientId()); dto.setContent(result.getViewUrl()); // 删除临时文件 FileUtil.del(savedFile); } String params; try { params = new ObjectMapper().writeValueAsString(dto); } catch (JsonProcessingException e) { log.error("params is dto-> {}",dto.toString()); throw new CommonException("参数有误"); } if (idList.size() < BATCH_SIZE) { tencentCloudImUtil.batchSendMsg(dto.getSyncOtherMachine(), accountId, idList, params); } else { for (int i = 0; i < idList.size(); i += BATCH_SIZE) { List<String> msgList = idList.subList(i, Math.min(i + BATCH_SIZE, idList.size())); tencentCloudImUtil.batchSendMsg(dto.getSyncOtherMachine(), accountId, msgList, params); //每秒限制200条 try { Thread.sleep(2000); } catch (InterruptedException e) { log.error("批量发送时异常 -->{}",e.getMessage()); } } } //执行完后删除相关上传文件 delTosFile(dto); } 这是我通过你给的相关信息写的代码 我是一个java开发工程师 请你分析是否合理 这是腾讯接口的请求频率限制 批量发单聊消息同时存在每秒接口请求次数和每分钟发送消息条数限制: 默认接口请求频率限制:200次/秒。 默认发送消息条数限制:12000条/分钟。按接收消息数量计算条数,若一次发送给500个用户,计作500条。 调用频率 请你分析 我写的是否合理 不合理的话 请你给出
最新发布
03-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值