集合中的fail-fast
fail-fast
其实是一种设计思想,即在程序出现错误时不再继续向下执行,而是抛出异常退出。
1 2 3 4 5 for (String name : names) { if (...) { names.remove(name); } }
上面这段代码就会触发fail-fast
错误,抛出异常ConcurrentModificationException
。
产生的原因 :增强的for循环本质上是一个语法糖,实际上是通过迭代器实现。
通过查看报错位置可以发现,抛出异常的原因是modCount
和expectedModCount
两个变量不相同。
modCount
:成员变量,记录集合被修改的次数
expectedModCount
:内部类Itr
的成员变量,表示这个迭代器预期该集合被修改的次数,在Itr
被创建时被初始化为当前modCount
值,只有通过迭代器修改集合才会更新该值
而上面的remove
代码的核心部分fastRemove
更新了modCount
,但是由于增强for循环创建了Itr
,而没有使用迭代器去删除元素,就导致了modCount
和expectedModCount
不一致。
解决方法 :使用普通for循环...
ArrayList
概述
RandomAccess
:标识类,并无实现,标识该类支持快速随机访问
Cloneable
:可以调用clone()
方法克隆对象
java.io.Serializable
:可序列化
和 LinkedList 的比较
是否线程安全
底层数据结构
增删查的复杂度
内存空间占用
ArrayList扩容机制
1、ArrayList提供了三个构造函数
要注意的是如果使用无参构造,底层的数组是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,结合后面的扩容机制可以发现当DEFAULTCAPACITY_EMPTY_ELEMENTDATA
第一次add元素时就会将扩容至DEFAULT_CAPACITY
(10)。
而EMPTY_ELEMENTDATA
是真实空的数组(因为只有传进来的容量为0的时候才会赋该值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public ArrayList (int initialCapacity) { if (initialCapacity > 0 ) { this .elementData = new Object [initialCapacity]; } else if (initialCapacity == 0 ) { this .elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException ("Illegal Capacity: " + initialCapacity); } }public ArrayList () { this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }public ArrayList (Collection<? extends E> c) { Object[] a = c.toArray(); if ((size = a.length) != 0 ) { if (c.getClass() == ArrayList.class) { elementData = a; } else { elementData = Arrays.copyOf(a, size, Object[].class); } } else { elementData = EMPTY_ELEMENTDATA; } }
2、每次调用add
函数都会经历一次"扩容"(可能没有真实发生)
1 2 3 4 5 public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; }
minCapacity
:完成add操作的最小扩容量
1 2 3 private void ensureCapacityInternal (int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
1 2 3 4 5 6 7 private static int calculateCapacity (Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
1 2 3 4 5 6 7 private void ensureExplicitCapacity (int minCapacity) { modCount++; if (minCapacity - elementData.length > 0 ) grow(minCapacity); }
实际进行扩容的函数是grow(int)
,经过一系列的判断后通过Arrays.copyOf()
进行扩容。
newCapacity
:扩容机制指定的扩容量(原容量1.5倍)
MAX_ARRAY_SIZE
:最大扩容量(Integer.MAX_VALUE-8
)
1 2 3 4 5 6 7 8 9 10 11 private void grow (int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1 ); if (newCapacity - minCapacity < 0 ) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0 ) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
1 2 3 4 5 6 7 private static int hugeCapacity (int minCapacity) { if (minCapacity < 0 ) throw new OutOfMemoryError (); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
总的来说扩容机制比较简单,主要是存在多个capacity
需要比较,需要弄清这些capacity
所代表的含义。
几个重要的函数源码
1 2 3 4 5 6 7 8 9 10 public void add (int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1 ); System.arraycopy(elementData, index, elementData, index + 1 , size - index); elementData[index] = element; size++; }
提供给外部的显示扩容函数ensureCapacity
注意所有的扩容都出现了DEFAULTCAPACITY_EMPTY_ELEMENTDATA
(第一次扩容的特殊性)
1 2 3 4 5 6 7 8 9 10 11 12 public void ensureCapacity (int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } }