ArrayList
ArrayList的状态
- 实例状态:
1 | /** |
- 类级状态
1 | /** |
ArrayList 内部使用 Object[] 来保存添加到其中的元素。
但是,ArrayList 是支持泛型,那么可不可以使用这个泛型参数来创建对象数组。
Java 是不允许创建泛型数组的。所有内部数据都是保存在 Object 数组中。
1 | public class ArrayList<E> extends AbstractList<E> |
ArrayList 的构造函数
- ArrayList()
1 | /** |
- ArrayList(int initialCapacity)
1 | /** |
- ArrayList(Collection<? extends E> c)
1 | /** |
fail-fast iterators
Iterator 接口
集合类的根接口 Collection 继承自 Iterable 接口,这个接口方法
1 | Iterator<E> iterator(); |
这个接口会返回一个 Iterator 类型的对象。可以通过这个接口遍历 Collection 对象,同时这个接口提供了一个 remove 方法,可以在遍历对象的同时删除对象中的元素。
fail-fast
ArrayList 类的父类 AbstractList 有一个字段: modCount 这个字段用来,跟踪 List 对象的被修改情况,
1 | protected transient int modCount = 0; |
文档描述如下:
The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
用来记录 list 的结构性的修改。结构性的修改包括 list 的size 变化,或者是其它扰动(perturb)
This field is used by the iterator and list iterator implementation returned by the iterator and listIterator methods. If the value of this field changes unexpectedly, the iterator (or list iterator) will throw a ConcurrentModificationException in response to the next, remove, previous, set or add operations. This provides fail-fast behavior, rather than non-deterministic(非确定性) behavior in the face of concurrent modification during iteration.
这个字段的主要用途是用来提供 iterator 的 fail-fast 行为(behavior), 在 iterator 方法 next, remove 中都会检测 list 对象的 modCount 这个字段是否被修改,如果被修改了,则 直接抛出 ConcurrentModificationException 异常
fail-fast 与 线程安全
iterator 的 fail-fast 机制 并不能保证线程安全。
java.util.ArrayList.Itr 类的 next 方法的实现,
1 | public E next() { |
由上面的代码及分析可知,fail-fast 机制,由于没有线程同步的保证,所以它并不是完全精确的行为。
那么,iterator 采用 fail-fast 机制的目地或者作用是什么呢?
关于 fail-fast wiki 其中提到 fail-fast 机制的使用场景,其实也表达的它的使用目地。 归结起来就是:错误检测和提高容错能力。
What is fail safe and fail fast Iterator in Java?
ArrayList javadocs 其中有关于 ArrayList 线程同步问题和fail-fast 的相关介绍。
structurally modified
A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.
增加,删除元素,或者显示修改底层的数据的大小,都是structural modification
java.util.ArrayList.SubList
ArrayList 方法 subList
1 | public List<E> subList(int fromIndex, int toIndex) |
返回一个 List 的视图。其操作的还是当前的 List 对象。这个方法的主要用途就是:
This method eliminates the need for explicit range operations (of the sort that commonly exist for arrays). Any operation that expects a list can be used as a range operation by passing a subList view instead of a whole list. For example, the following idiom removes a range of elements from a list:
list.subList(from, to).clear();
如果需要操作一个list的某个范围的数据,可以使用上面的方法。
注意
当使用 subList 方法返回 list的一个视图的时候,在使用 sublist 时,就不能再操作 list, 否则会报 java.util.ConcurrentModificationException
remove 操作
如果待删除的元素是null,下面的操作也能成功,会找 list 中 null的元素。
删除指定索引的元素
public E remove(int index)
删除指定对象
public boolean remove(Object o)
删除指定collections中的元素
public boolean removeAll(Collection<?> c)
删除collection 以外的元素
public boolean retainAll(Collection<?> c)
ArrayList 对 null 的支持
ArrayList实现中,不会对参数为 null ,也就是空对象,进行检测,包括 add ,set , remove, contains 等方法。也就是它们基本不会因为操作的元素的null,而抛 NullPointerException。其实这也反映了 ArrayList 的本质作用,作为容器,就应该可以容纳各种对象,而 null 对象,也是对象的一种。
关于集合类支持 null 值的 thread:
Why Null value is allowed in ArrayList,Vector,Set or HashMap?
ArrayList 文档描述:
Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null.
扩容策略
- 公共接口
1 | public void ensureCapacity(int minCapacity) { |
如果 ArrayList 内部的存储数据的数组: elementData 不为空(elementData != EMPTY_ELEMENTDATA),则 这个方法调用可以传递 > 0 的任何数值,作为扩容的参数。否则,传递的参数,必须大于ArrayList类的初始默认的容量:DEFAULT_CAPACITY = 10,就是说如果此时传递参数为 5, 则这个方法什么都不会做。
- 内部的扩容方法
1 | private void ensureCapacityInternal(int minCapacity) { |
如果elementData是null,则扩容取 DEFAULT_CAPACITY 和 minCapacity 中较大的值。如果它minCapacity 小于 DEFAULT_CAPACITY 这个方法会按 DEFAULT_CAPACITY 去扩容,而上面公共方法中则什么都不会做。这是这两个方法的区别。
- ensureExplicitCapacity
1 | private void ensureExplicitCapacity(int minCapacity) { |
调用 grow 方法
1 | private void grow(int minCapacity) { |
a. newCapacity 是 当前容量的 1.5 倍
newCapacity = oldCapacity + (oldCapacity >> 1)
b. newCapacity 小于 minCapacity,则新容量
newCapacity = minCapacity
c. newCapacity 超过数组的最大值 MAX_ARRAY_SIZE
newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE
注意 minCapacity 如果 < 0, 则会抛出 OutOfMemoryError 的错误。其实这种情况应该是溢出了, 例如: minCapacity = Integer.MAX_VALUE + 20 此时的 minCapacity 其实是 -2147483629 所以会出现 < 0 的状况。
clone
ArrayList重写的 clone 方法,实现底层数组的浅clone.返回一个新的ArrayList。注意这个clone后的对象的 modCount 会被重置成 0.
System.arraycopy
这是一个 native 的方法:
1 | public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); |
newCapacity = oldCapacity * 1.5
Vector
1
2
3int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);newCapacity = oldCapacity * 2
fail-fast iterator
Vector 有一个返回 Enumeration 的方法 elements,返回的 Enumeration 不是 fail-fast, 但是 iterator 和 listIterator 返回的 iterator 是 fail-fast的
ArrayList 没有 elements 方法,它的 iterator 也都是 fail-fast 的。
Stack
JDK 中提供的 java.util.Stack类,基于 Vector 实现的,也是同步的,但是,其父类已经都不推荐使用了,所以:
A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. For example:
Deque<Integer> stack = new ArrayDeque<Integer>();