Vector
源码基于1.8.0_112
Vector使用的相对较少,结构与ArrayList几乎一样。Vector的public方法大多为synchronized,是线程安全的。
原理 :ArrayList内部使用一个数组存储放入的数据,数组使用默认大小初始化(也可以自定义),当数组无法再放入更多的对象时,数组会扩大到原来的2倍或者使用增加扩容系数的大小。
成员变量
大致浏览,结合具体方法来了解具体含义
// 最重要的一个数组,所有存入Vector的对象都别存入该数组
protected Object[] elementData;
// elementData数组中实际存储元素的个数,相当于ArrayList中的size
protected int elementCount;
// 每次增加的容量
protected int capacityIncrement;
// 数组最大空间,某些虚拟机可能会预留一些数组空间存储元数据,所以空出8位
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// list被修改的次数,该变量继承于AbstractList
protected transient int modCount = 0;
构造方法
先阅读默认构造函数,其他构造函数可在阅读完具体操作后再回来阅读
add(E e)
每次加入对象前,会先进行数组大小检查,然后决定是否扩容,详细代码解析可参考 ArrayList
add为同步方法
扩容:
默认 capacityIncrement=0 ,每次扩容为原来的2倍
指定capacityIncrement,则每次增加 capacityIncrement 的大小
图解
代码
/**
* 添加元素,几乎与ArrayList一样
* 不同点
* 1. Vector的add方法为synchronized,线程安全
* 2. Vector的扩容机制不一样
*
* @param e
* @return
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 扩容
// 默认 capacityIncrement=0 ,所以每次扩容为原来的2倍
// 指定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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
get(int index)
很简单,类似于数组的访问
get为同步方法
代码
/**
* 随机访问方法
* @param index
* @return
*/
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
remove(Object o)
把删除位置之后的数组往前移动一位,并且将最后一位设为null
虽然调用顺寻与ArrayList稍有不同,但是基本逻辑一样
remove为间接的同步方法
图解
/**
* 删除操作
* @param o
* @return
*/
public boolean remove(Object o) {
return removeElement(o);
}
public synchronized boolean removeElement(Object obj) {
modCount++;
// 循环查找到obj的下标
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
public int indexOf(Object o) {
return indexOf(o, 0);
}
public synchronized int indexOf(Object o, int index) {
if (o == null) {
for (int i = index ; i < elementCount ; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = index ; i < elementCount ; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
// 把删除位置之后的数组往前移动一位,并且将最后一位设为null
// 例如,A,B,C,D --> A,C,D,null
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
Vector中synchronized对性能的影响
jdk1.6中引入轻量级锁的新型锁机制。
轻量级锁:对象信息中用两位来存储锁状态,通过cas操作来加锁或者解锁。
重量级锁:我们平常所说的锁,会从用户态切换到核态,挂起所有线程。
轻量级锁在没有竞争的情况下具有相当好的性能。但是在存在竞争的情况下轻量级锁加锁失败,则锁会膨胀为重量级锁,两种操作都执行了,效率还不如重量级锁。
这块内容不详细展开(自行阅读《深入理解java虚拟机》),可以简单理解为,在没有多个线程同时操作一个对象的时候,把此操作当做非同步的方法操作。
我们做一个简单的测试
十次测试花费的时间几乎一样
// 简单测试代码
public class TestArrayListAndVector {
public static void main(String[] args) {
TestArrayListAndVector test = new TestArrayListAndVector();
for (int i = 0; i < 10; i++) {
test.testArrayList();
// test.testVector();
}
}
private void testArrayList() {
int count = 99999;
Random random = new Random();
long start = System.nanoTime();
List list = new ArrayList();
for (int i = 0; i < count; i++) {
list.add(random.nextInt(9999));
}
for (int i = 0; i < count; i++) {
list.get(random.nextInt(9998));
}
for (int i = 0; i < count; i++) {
list.remove(0);
}
System.out.println("ArrayList");
System.out.println(System.nanoTime() - start);
}
private void testVector() {
int count = 99999;
Random random = new Random();
long start = System.nanoTime();
List list = new Vector();
for (int i = 0; i < count; i++) {
list.add(random.nextInt(9999));
}
for (int i = 0; i < count; i++) {
list.get(random.nextInt(9998));
}
for (int i = 0; i < count; i++) {
list.remove(0);
}
System.out.println("Vector");
System.out.println(System.nanoTime() - start);
}
}
总结
- Vector中使用了数组,所以随机访问的性能很好
- 由于当内部数组容量不足时需要扩容,所以Vector的插入性能一般
- 当确定Vector会在短时间内超过某个容量时,可以直接指定初始化的容量,来避免频繁扩容带来的性能损失
- Vector的源码几乎与ArrayList一样,只是Vector多数方法为同步方法,保证线程安全
- 单线程环境下Vector的性能与ArrayList几乎一样
补充
有位XX小号的吧友提出Vector已经过时,这里再做一点补充
- Vector先于List出现,所以设计上有一定的缺陷,并不能与List接口很好的结合(这可能是不推荐使用的重要原因之一)
- Vector中使用了大量的同步方法,在线程竞争激烈的情况性能较差(之后再分析SynchronizedList和CopyOnWriteArrayList作为替代集合)