本文以Java8为例java
AbstractList是抽象类,里面有众多List基本方法的基本实现。事实上JDK中有众多相似AbstractXXX的类,他的思想是就是把众多公共的实现提取出来,AbstractXXX面向的是工具类的开发者,咱们能够基于AbstractList来实现本身的List。而ArrayList只是JDK已经帮咱们实现好的,面向工具类的使用者。数组
很少说,多态。父类的引用指向子类的实例。安全
RandomAccess即随机访问。bash
先给出结论:集合中元素的访问分为随机访问和顺序访问。 随机访问相似于数组下标的访问,顺序访问相似于链表的访问,随机访问直接经过下标取值性能较好,顺序访问以迭代器遍历性能比较好。 查看RandomAccess接口,仅仅是一个空接口而已。app
原来,RandomAccess用意仅仅是一个标记而已,即标记接口,用于判断List是否为RandomAccess的实现。以Collections的binarySearch方法为例:dom
因此RandomAccess仅仅是一个类的标记而已。函数
Cloneable是否可克隆,查看Cloneable接口 工具
这里会有一个疑问:什么是可克隆?源码分析
先给出结论:clone方法是基类Object的方法,只有实现了Cloneable接口的类才可使用clone方法,不然会抛CloneNotSupportedException异常。下面是某版本JDK的clone方法的源码,会发现若是不是Cloneable的子类则会抛异常。性能
protected Object clone() throws CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException("Class " + getClass().getName() +
" doesn't implement Cloneable");
}
return internalClone();
}
/*
* Native helper method for cloning.
*/
private native Object internalClone();
复制代码
然而查看JDK1.8的clone方法,仅仅是一个本地方法而已,他又是怎么作到抛异常的呢?
protected native Object clone() throws CloneNotSupportedException;
复制代码
原来在JDK1.8中,若是咱们查看JVM的C++源码,会发现“对象是否实现了cloneable接口抛异常”的动做实如今了JVM层中。
因此ArrayList经过实现了cloneable接口,支持clone方法。
另外,有说法认为Java的类经过实现Cloneable来标识出对象是否可使用clone是一种很糟糕的设计,未实现Cloneable确对外暴露clone方法的规则很奇怪。按照如今的视角来看,若是给类从新设计下“是否可克隆”这个规则,该怎么实现好呢?这里存疑。
一样也是标记接口,表示该类的对象是否能够序列化。
// 若初始化时未指定容量大小,默认为10
private static final int DEFAULT_CAPACITY = 10;
// 一个空数组而已
private static final Object[] EMPTY_ELEMENTDATA = {};
// 也是一个空数组,只不过与EMPTY_ELEMENTDATA作区分。
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示初始化是无参构造
// 而EMPTY_ELEMENTDATA是有参构造,只不过参数是0
// 之因此用来区分是由于无参构造是默认容量为10
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存储对象的容器
transient Object[] elementData;
// 存储对象的多少
private int size;
复制代码
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) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
复制代码
三个构造方法参数分别是容量、空、集合。经过观察代码会发现容量为0时, this.elementData有两种状况
构造函数传入initialCapacity时this.elementData = EMPTY_ELEMENTDATA;
无参构造函数时this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
关于这两个属性,源码给的解释是
/**
* Shared empty array instance used for empty instances.
* 用于空实例的共享空数组实例
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
* 共享空数组实例,用于默认大小的空实例。
* 咱们将其与EMPTY_ELEMENTDATA区分开来,以了解添加第一个元素时应该膨胀多少
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
复制代码
DEFAULTCAPACITY_EMPTY_ELEMENTDATA和EMPTY_ELEMENTDATA其实就是实例化ArrayList方式的区分。
DEFAULTCAPACITY_EMPTY_ELEMENTDATA在后面add方法中会详细解释,其实这里能够理解为DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示实例化时是无参构造,未指定容量,在调用add方法时这种状况会默认此刻容量为10。
而EMPTY_ELEMENTDATA表示在咱们实例化对象时指定了容量就是0。
public boolean add(E e) {
// 内部确保容量
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
// 内部确保容量
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
复制代码
add方法的核心ensureCapacityInternal,内部确保容量。
内部确保容量即add元素时,程序里经过当前的size自动判断当前是否须要扩容。与其相对应的是显示确保容量。ensureCapacityInternal源码分析以下
// minCapacity表示容器要确保支持的容量
// elementData的长度至少要大于等于minCapacity,是需求的最低大小
// 好比add时 minCapacity就是size+1
// ensureCapacity调用ensureCapacity显示扩容时,minCapacity就是传入的参数
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 该方法仅仅是多作了一步判断,若是是以new ArrayList()无参构造函数实例化的对象,且是第一次add元素,则默认的最小容量为10。
// 若是没有默认容量,由于容器最初容量小,add元素时1.5倍的扩容方式会形成频繁的扩容。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 显示确保容量,在ensureCapacityInternal中调用只是方法的公用
// “显示”的含义主要体如今ensureCapacity方法的调用,ensureCapacity是人为的显示扩大容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 实际扩容,默认按1.5被扩容,若是1.5倍仍是小于minCapacity,则以minCapacity为准
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 容量扩容为原有的1.5倍
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);
}
复制代码
若是未指定容量,add方法以10为初始值,每次扩容以1.5倍扩容。因此若是有大量数据add,也会触发屡次扩容。而每次扩容其实是将数组内容复制到新的数组里。
精简化容器。ArrayList在实际使用中,elementData的长度大于等于size,而trimToSize会将elementData按照size从新复制一份给elementData,最小化ArrayList实例的存储。若是实例内容不变,能够调用该方法节约内存。
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
复制代码
至关于扩容,也是咱们前面提到的显示确保容量。
给定一个minCapacity,保证ArrayList能够容纳至少minCapacity数量的对象。
public void ensureCapacity(int minCapacity) {
// 以此来判断咱们实例化时是否为 new ArrayList()
// 若是是,他的默认长度在add时就是10,不须要扩容
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
复制代码
前面已经作过度析,经过实现Cloneable接口,使得clone方法可用。重写clone方法,复制出一份数组,但其自己仍是浅拷贝,数组里引用的对象没有被复制。
modCount表示改动次数,新克隆的对象置为0。
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable throw new InternalError(e); } } 复制代码
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
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
}
复制代码
remove其实就是将index后面的元素向前移动一位。因此若ArrayList很大,不适合使用remove方法。
Vector就是线程安全版的ArrayList,它的实现与ArrayList基本相似,这里说下比较明显的3点区别
Vector针对容器的操做都加上了synchronized关键字来保证线程安全,譬如:
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
modCount++;
ensureCapacityHelper(minCapacity);
}
}
复制代码
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
复制代码
相比ArrayList,Vector多了一个构造方法,多传入一个参数capacityIncrement,能够用来指定每次扩容的增量。
另外new Vector()默认容量也是10,只不过在实例化时数组的长度就已是10了。而ArrayList中是add第一个元素以后数组的长度才会变成10。
public Vector() {
this(10);// 默认容量为10
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
复制代码
Vector的扩容方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 若是有capacityIncrement,按照capacityIncrement扩容,不然按照当前容量的一倍扩容
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
复制代码
Vector的扩容默认是按一倍扩容,若是指定capacityIncrement,则按照capacityIncrement扩容。
然而就我而言,对Vector应用几乎没有应用到。若是须要线程安全的List通常使用的是CopyOnWriteArrayList。
LinkedList就是个双向链表结构,每一个节点维护其前置节点和后驱节点。
LinkedList的源码比较简单就不作分析了。
ArrayList是线性结构支持RandomAccess,LinkedList是链式结构。
实现RadomAccess随机访问,使得ArrayList在一些方法中是以数组下标取值。
实现CloneAble支持克隆,浅拷贝。
默认容量为10,以1.5倍递增的方式扩容。
数据量大时并不适合remove。