Java集合框架(1) - ArrayList篇
Java内置了很多数据结构的实现,比如ArrayList、LinkedList、ArrayDeque等。那么这篇博客主要讲解ArrayList的实现。见名知意,ArrayList首先是一个列表,它是靠数组去实现的。那么它和我们平时使用的数组最大的不同是它可以动态的去扩容。
继承与实现体系
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
}
AbstrictList
类:这个类里面有一个很重要的属性:modCount,这个是List集合fail-fast
的关键RandomAccess
接口:RandomAccess
是一个空接口,它标识着ArrayList支持随机化访问Cloneable
接口:Cloneable
也是一个空接口,它意味着ArrayList支持克隆方法Serializable
接口:Serializable
是一个空接口,它代表着ArrayList支持对象序列化
ArrayList的常量
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
serialVersionUID
:序列化对象时的序列号DEFAULT_CAPACITY
:ArrayList的默认大小EMPTY_ELEMENTDATA
:用户指定的空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
:默认的空数组MAX_ARRAY_SIZE
:ArrayList的容量上界,但并不是最大值。
ArrayList的变量
transient Object[] elementData;
private int size;
- elementData:存放元素的数组
- size:有效元素的个数
ArrayList的构造函数
-
无参构造:在调用无参构造的时候,elementData的值是默认的空数组,size的值是0
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
-
有参构造:
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); } }
- 如果用户给定的容积小于0,则直接抛出异常;
- 如果用户给定的容积等于0,则elementData的值是用户的空数组;
- 如果大于0,则elementData的值是用户分配容量的数组
-
有参构造:将集合c的元素转移到ArrayList中
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; } }
ArrayList的扩容机制
也就是所ArrayList没有达到上界MAX_ARRAY_SIZE
的时候,将容量扩容到之前的1.5倍,如果1.5倍还不够用,那么直接扩展到实际需要的容量。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
fail-fast
我们都说ArrayList是fail-fast的,查看源码的时候我们会发现,当调用remove
,add
这种改变自身结构的方法的时候都会有modCount++
。其实平时没什么用,但是当你使用迭代器遍历ArrayList的时候这个是十分重要的,迭代器会记住当前modCount
的值。迭代器会认为modCount的值一直不会变化,如果发生变化就会立马抛出异常。因为改变结构这种操作在多线程的情况下很不安全,这也就是所谓的fail-fast。细想一下,我们在使用foreach循环的时候,如果你在里面有add
或者remove
操作,会立马抛出异常,因为foreach底层是靠迭代器实现的。