ArrayList底层使用数组进行存储数据,具有查询速度快、读写速度慢的特性。
数组空间的每次扩展都会进行一次数组复制,我们可以通过构造器指定数组大小避免频繁复制数组导致的性能下降。
另一个删除方法是通过对象删除,由于需要先查询索引。因此性能上还是按照索引删除快。
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; }可以看到查找直接通过索引即可查询相关数据, 而插入(非尾部插入)和删除均需要复制数组,因此性能要比查询慢。
for循环删除元素时会有索引错乱问题,iterator则不会。
首先iterator进行迭代时取下一个元素,把游标移动到当前元素的下一个索引。
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; //这里会把当前游标赋值给lastRet return (E) elementData[lastRet = i]; }进行删除时,删除当前元素后。数组会整体向前移动,当前索引的位置元素就变成了移除前的下一个索引的元素。当前索引重新赋值给游标,下次用next()方法取到的游标仍然是当前索引。
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(); } }