错误方法1,原本想删除所有>= threshold的值,但是会漏删一些元素,达不到想要的效果
public static List<Integer> filterUnderThreshold(final List<Integer> values, final int threshold) { List<Integer> returnValues = new ArrayList<>(values); for (int i = 0; i < returnValues.size(); i++) { if (returnValues.get(i) >= threshold) { returnValues.remove(i); } } return returnValues; }
原因在于,执行remove(int index )后,index 之后的所有元素依次向前移一位。
错误方法2,会抛ConcurrentModificationException
public static List<Integer> filterUnderThresholdByForeach(final List<Integer> values, final int threshold) { List<Integer> returnValues = new ArrayList<>(values); for (Integer value : returnValues) { if (value >= threshold) returnValues.remove(value); } return returnValues; }
先看看remove(Object o)实现
public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { // 判断是否存储了 null fastRemove(index); return true; } } else { // 遍历ArrayList,找到“元素o”,则删除,并返回true for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { // 利用 equals 判断两对象值是否相等(equals 比较值,== 比较引用) fastRemove(index); return true; } } return false; }
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; // 将最后一个元素设为null }
可以看到,调用remove(Object o)时,modCount改变了,但是expectedModCount却没有变,当下一次next()执行时,会调用checkForComodification(),由于modCount != expectedModCount,抛出ConcurrentModificationException
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
正确方法1,在iterator中删除
public static List<Integer> filterUnderThresholdByIterator(final List<Integer> values, final int threshold) { for (Iterator<Integer> it = values.iterator(); it.hasNext(); ) { int value = it.next(); if (value >= threshold) { it.remove(); // ok } } return values; }
这里调用的是ArrayList的内部类Itr的remove()方法
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
这里会给expectedModCount重新赋值,所以当执行next()时,不会出错。
正确方法2,先加入到一个临时列表中,然后整体删除
public static List<Integer> filterUnderThresholdByRemoveall(final List<Integer> values, final int threshold) { List<Integer> templist = new ArrayList<>(); for (int value : values) { if (value >= threshold) { templist.add(value); } } values.removeAll(templist); return values; }
调用的是
public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false); }
private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; try { for (; r < size; r++) if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; }
这个方法没有使用ArrayList的内部迭代器Itr,也就不会有modCount != expectedModCount的问题。
正确方法3,用lambda表达式,也是相对简单简洁的方法,可以视为正确方法1的简化
public static List<Integer> filterUnderThresholdByLambda(final List<Integer> values, final int threshold) { values.removeIf(value -> value >= threshold); return values; }
这里调用了
default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed; }
以上就是ArrayList删除元素的相关操作。