List集合注意事项
一、添加元素时,如果元素为数字类型时,会有一个自动装箱的操作,也就是new Integer(20)。
list.add(20);//等价于list.add(new Integer(20));
二、删除元素时,如果元素为数字类型,想要通过元素来进行删除,需要做一个装箱操作,new Integer()。
list.remove(new Integer(20));
三、remove()方法陷阱
List list = new ArrayList(); list.add(20); list.add(20); list.add(20); list.add(50); list.add(60); for (int i = 0; i < list.size(); i++) { if (list.get(i).equals(20)) list.remove(i); } System.out.println(list);
输出结果:[20, 50, 60]。
这时我们会发先集合中的20并没有被完全删除,这是为什么呢?
因为在List中,当我们去移除索引i位置的元素时,i位置右边的元素就会向左移动,也就是索引依次-1。所以说: 第一次循环时集合为:[20,20,20,50,60],删除的索引值为0,也就是[20,20,20,50,60]。 第二次循环时集合为:[20,20,50,60],删除的索引为1,也就是[20,20,50,60]。 第三次循环时集合为[20,50,60],删除的索引为2,而索引2位置元素不为20,所以不做删除。 这个时候我们会发现,list长度变为了3,而下次循环时i=3,不满足条件i<list.size(),所以这时循环结束。
解决方案:
1、删除元素时,将同步调整for循环索引
for (int i = 0; i < list.size(); i++) { System.out.println(list.size()); if (list.get(i).equals(20)) { list.remove(i); i--; } }
输出结果:[50,60]
2、倒序删除
for (int i = list.size() - 1; i >= 0; i--) { if (list.get(i).equals(20)) { list.remove(i); } }
输出结果:[50,60]
3、使用迭代器iterator
Iterator it = list.iterator(); while (it.hasNext()) { if (it.next().equals(20)) { it.remove(); } }
输出结果:[50,60]
四、使用增强for删除list元素
for (Object o : list) { list.remove(o); }
抛出异常:java.util.ConcurrentModificationException。
增强for(foreach)实际上是对Iterable,hasNext(),next()方法的简写。通过List.iterator()源码可知,它返回了一个Itr对象。
public Iterator<E> iterator() { return new Itr(); }
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; Itr() {} public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } 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(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
通过代码可知,Itr是ArrayList内部的一个私有类,实现了Iterator接口的功能。在next()方法和remove()方法中都调用了方法checkForComodification(),方法内部判断modCount != expectedModCount,则抛出new ConcurrentModificationException()异常。
再看ArrayList的remove()方法:
public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { 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; // clear to let GC do its work }
我们会发现在调用list的remove()方法时,针对modCount做了修改,而没有对list的迭代器内部的expectedModCount变量做操作,这时判断条件成立,就抛出了new ConcurrentModificationException()异常。
再看ArrayList内部迭代器内的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(); } }
在调用ArrayList.remove()方法后,对expectedModCount 做了同步操作,所以不会出现此问题。

浙公网安备 33010602011771号