1、你真的会用List的remove方法吗?
首先看下面这个例子:
public class ListRemoveDemo {
public static void main(String[] args) {
List<String> sourceList = new ArrayList<>();
sourceList.add("Rose");
sourceList.add("Jack");
sourceList.add("Cooper");
sourceList.add("Jay");
//此时打印sourceList输出: [Rose,Jack,Cooper,Jay]
for (int i = 0; i < sourceList.size(); i++) {
String curStr = sourceList.get(i);
if (isNeedRemove(curStr)) {
sourceList.remove(curStr);
}
}
//此时打印sourceList输出: [Rose,Cooper,Jay]
}
private static boolean isNeedRemove(String s) {
if ("Jack".equalsIgnoreCase(s)) {
return true;
}
return false;
}
}
上面例子中,使用isNeedRemove方法来决定List哪些元素应该被移除,从上面的输出结果来看, 原始list:[Rose,Jack,Cooper,Jay] 删除Jack后 最后的list: [Rose,Cooper,Jay] 乍一看,非常完美了达到了想要实现的效果。
再看下面这个例子:
public class ListRemoveDemo {
public static void main(String[] args) {
List<String> sourceList = new ArrayList<>();
sourceList.add("Rose");
sourceList.add("Jack");
sourceList.add("Jack");
sourceList.add("Jay");
//此时打印sourceList输出: [Rose,Jack,Jack,Jay]
for (int i = 0; i < sourceList.size(); i++) {
String curStr = sourceList.get(i);
if (isNeedRemove(curStr)) {
sourceList.remove(curStr);
}
}
//此时打印sourceList输出: [Rose,Jack,Jay]
}
private static boolean isNeedRemove(String s) {
if ("Jack".equalsIgnoreCase(s)) {
return true;
}
return false;
}
}
在这个例子中,sourceList为[Rose,Jack,Jack,Jay],想要达到的效果是删除为Jack的元素输出为[Rose,Jack,Jay],而实际输出结果为[Rose,Jack,Jay]。 很显然,有一个Jack没有成功被remove掉。
这里的例子只是简单的模拟出remove的坑,想象一下,isNeedRemove方法是某个业务判断,对于List中的不同的元素可能都会返回需要从列表中删除,这时就会遇到上面例子中存在的问题。
那么,问题出在哪?
其实,原因很简单,通过查看ArrayList的remove的源码可以知道,remove过程实际上是会进行一次数组“平移”,找到需要删除的元素的index,将[index+1, list.size()-1]向前移动一位。
// ArrayList.java
// remove方法最终会调用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方法将index + 1位置及其后的元素向前移动一位,最后将数据elementData末尾的元素设置为null,且size减1。
通过这里可以看出,每次remove一个元素后,后面的节点元素会整体前移一位,而使用for循环的i却加1了,上面的案例的remove过程如下:
2.怎么样正确的使用List的remove方法?
1)倒序删除法
上面错误的方式是从前往后进行判断后remove,从List的尾部遍历到头部进行判断remove
2)新建一个List
新建一个List来存放需要保留的元素
3)使用迭代器Iterator
public class ListRemoveDemo {
public static void main(String[] args) {
List<String> sourceList = new ArrayList<>();
sourceList.add("Rose");
sourceList.add("Jack");
sourceList.add("Cooper");
sourceList.add("Jay");
//此时打印sourceList输出: [Rose,Jack,Jack,Jay]
Iterator<String> iterator = sourceList.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (isNeedRemove(next)) {
// 注意这里是iterator的remove
iterator.remove();
}
}
//此时打印sourceList输出: [Rose,Jay]
}
private static boolean isNeedRemove(String s) {
if ("Jack".equalsIgnoreCase(s)) {
return true;
}
return false;
}
}