- 为什么在使用ArrayList使用fori方式遍历时删除某个元素会有漏网之鱼的元素出现
1.首先给出测试代码
package com.yuhl.List;
import java.util.ArrayList;
import java.util.List;
/**
* @author yuhl
* @Date 2020/10/18 12:42
* @Classname ArrayList001
* @Description ArrayList使用fori方式遍历删除陷阱源码分析
*
* 现象为删除b元素,但是遍历过程为只遍历了:a,b(被删除了所以不打印),d,e
* 为什么c(c成了漏网之鱼)没有被打印呢?
*/
public class ArrayList001 {
public static void main(String[] args) {
//构建ArrayList集合
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
delete1(list);
}
/**
* 使用for循环删除list中的元素b
*/
public static void delete1(ArrayList<String> list){
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
if ("b".equals(ele)) {
list.remove(ele);
continue;//如果被删除则不进行输出
}
System.out.println(ele);
}
}
}
2.分析【c元素】被漏掉的原因,源码分析
list.remove(ele);方法时调用底层的此方法fastRemove()方法
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
在这个方法中调用System.arraycopy方法
/**
src:当前需要被移动的对象
srcPos:当前需要被抵用的对象的数组开始索引
dest:移动的目的地对象
destPos:移动的目的地对象的开始索引
length:需要移动的长度
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
调用此arraycopy()方法前
list = {a,b,c,d,e};
i = 1;
调用此arraycopy()方法后
list = {a,c,d,e};
i++;//变成了2 此时list[2]=d刚好把c遗漏掉
此时list[2]=d刚好把c遗漏掉。
tips:
此处System.arraycopy()方法并未开辟新的空间存放数据,就是对从srcPos开始的元素依次往左移动。
3.解决方案
知道了出现问题的原因,找到解决的办法不难,可以使用倒序进行list的遍历,再次过程中删除元素无任何问题。
原因为:对于删除的元素,其后面元素会向前移动一个下标,但是,对于未遍历到的元素没有任何变动。
4.解决方案代码如下:
package com.yuhl.List;
import java.util.ArrayList;
import java.util.List;
/**
* @author yuhl
* @Date 2020/10/18 12:42
* @Classname ArrayList001
* @Description ArrayList使用fori方式遍历删除陷阱源码分析
*
* 现象为删除b元素,但是遍历过程为只遍历了:a,b(被删除了所以不打印),d,e
* 为什么c(c成了漏网之鱼)没有被打印呢?
*/
public class ArrayList001 {
public static void main(String[] args) {
//构建ArrayList集合
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
//delete1(list);//陷阱方法
delete2(list);//倒序解决方案
}
/**
* 使用for循环删除list中的元素b 陷阱方法
*/
public static void delete1(ArrayList<String> list){
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
if ("b".equals(ele)) {
list.remove(ele);
continue;//如果被删除则不进行输出
}
System.out.println(ele);
}
}
/**
* 使用for循环删除list中的元素b 倒序解决方案
*/
public static void delete2(ArrayList<String> list) {
for (int i = list.size()-1; i >= 0; i--) {
String ele = list.get(i);
if ("b".equals(ele)) {
list.remove(ele);
continue;//如果被删除则不进行输出
}
System.out.println(ele);
}
}
}
下一篇:ArrayList使用foreach方式遍历删除陷阱源码分析https://blog.csdn.net/fsjwin/article/details/109147146