本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看<活动链接>
前言
- 大数据时代谁掌握了数据,谁就是王者。今天我来分享一个有趣的bug---顽强的数据
业务表现
- 某一天我接了一个需求。要求前端传递一个列表数据然后我根据一定规则进行筛选。将剩余的数据返回至前端
- 这种需求对于我来说还是很得心应手的。立马就开始动起来的,根据条件判断不满足的删除。
- 三下五除二开发完成很自信的上线了。问题也就埋下了
- 随着业务发展之前的筛选开始暴露问题了。之前删除都是删除一个元素没发现问题。后来开始删除多个了。发现有部分数据很顽强删除不了。
问题定位
- 为了方便演示,下面我通过简化代码来展示。下面是我们实现的删除数据的情况 。 这里也不加条件了就是直接删除数据 。 打印之后你会发现list并不是空
public static void main(String[] args) throws InterruptedException {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(Double.valueOf(Math.random() * 100).intValue());
}
for (int i = 0; i < list.size(); i++) {
list.remove(i);
}
System.out.println(list);
}
- 是的,你没看错,奇数位的数据都被删除了。但是偶数位的数据顽强的活了下来。下面我们看看remove到底做了啥?
源码分析
System.arraycopy
这个函数可以会有读者陌生,这个方法是jdk提供的内部会调用C的东西。我们只需要知道这个函数的功能能就行了。他就是将数组从一个位置到另外一个位置的部分复制到新的数组的指定部分。谁白了就是复制。这里的作用就是将后续元素前移。因为ArrayList
内部是数组完成的所以需要我们自己处理数据前移- 当我们循环开始会先删除第一个元素index=0 ; 此时会将数据前移,从而48会被56替换。后面依次如下图
- 删除成功后会将48返回,循环会走到index=1 。 那么问题来了我们此时的list内部元素已经移动过了。此时的index=1在list中已经是24了。56所在的位置index=0 , 已经经历过依次洗礼了变成了安全位置。这也就是为什么我们删除不掉56的原因。
- 两张图片彻底的展示了10个元素的删除过程。偶数位因为数据已经被前移所以躲避了删除的命运。
扩展
- 在list中remove有两个方法。一个通过索引删除。一个通过对象元素删除;除此之外还有removeAll通过集合删除;removeIf根据条件判断删除。最后在iterator中还有一个remove方法。基本满足我们的各种需求
- 另外我们再来看看如下代码,remove(2) 我们想删除list中2这个元素。但是实际上删除的是list中第三个元素。这就是remove传参的问题。
- 我们只需要将2转换成object就会实现我们的需求
总结
- 对API的理解不能自以为是,如果想当然的话最终只会自己填坑
- 理解内部原理就不会贻笑大方
多谢点赞