Vector源码解析
继承接口
- 继承了
AbstractList
类,这个类也实现了 List 方法 - 实现了
Serializable
,能进行序列化操作 - 实现了
Cloneable
接口,能够使用 clone() 方法 - 实现了
RandomAccess
,说明 Vector 也支持随机访问,在 Vector 中,可以通过元素的索引快速获取元素对象,这就是快速随机访问。
成员变量
// 存储数据的数组,此数组的容量一般大于实际长度,后续未填充数据用 null 替代
protected Object[] elementData;
// 实际元素个数
protected int elementCount;
// 容量增量,当容量不足以容纳元素时,自动扩容的大小。如果此值<=0 则扩容一倍
protected int capacityIncrement;
// 最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造函数
// 构造一个空数组,默认容量10,容量增量为0。
public Vector() {
this(10);
}
// 指定初始容量,容量增量为0
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//指定初始容量,指定容量增量
public Vector(int initialCapacity, int capacityIncrement) {
// 调 AbstractList 的无参构造
super();
// 初始容量 < 0 抛出异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
// 初始化elementData,容量为指定的
this.elementData = new Object[initialCapacity];
// 设置容量增量
this.capacityIncrement = capacityIncrement;
}
//用指定集合初始化数组
public Vector(Collection<? extends E> c) {
//将c转换为Object数组
elementData = c.toArray();
//容量个数赋值
elementCount = elementData.length;
// c.toArray();可能返回的不是object数组,如果不是用copy方法再拷贝一次
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
核心方法
trimToSize()
将底层数组的容量调整为当前vector实际元素的个数,来释放空间。
public synchronized void trimToSize() {
//修改次数+1
modCount++;
//记录旧容量
int oldCapacity = elementData.length;
//如果实际元素个数<容量
if (elementCount < oldCapacity) {
//将数组长度修剪为实际元素个数,调用native方法拷贝
elementData = Arrays.copyOf(elementData, elementCount);
}
}
add过程分析
add(E e)
: 将指定的元素附加到此 Vector 的尾部
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
ensureCapacityHelper(int minCapacity):
判断元素个数是否越界,调用扩容函数
private void ensureCapacityHelper(int minCapacity) {
// 判断当前元素个数+1是否大于数组长度,大于则扩容
if (minCapacity - elementData.length > 0)
//传入元素个数
grow(minCapacity);
}
grow(int minCapacity)
:
扩容函数
private void grow(int minCapacity) {
//保存旧容量
int oldCapacity = elementData.length;
//扩容:新容量=旧容量+旧容量(当容量增量为0或者负数时)
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
//如果新容量还小于传入容量
if (newCapacity - minCapacity < 0)
//将扩容后的容量再次扩容为想要的最小容量
newCapacity = minCapacity;
//如果扩容后的容量大于临界值(integer的最大值-8),则进行大容量分配
if (newCapacity - MAX_ARRAY_SIZE > 0)
//返回integer最大值或者最大值-8
newCapacity = hugeCapacity(minCapacity);
//拷贝新数组到Vector
elementData = Arrays.copyOf(elementData, newCapacity);
}
/**
* 进行大容量分配
*/
private static int hugeCapacity(int minCapacity) {
//如果minCapacity<0,抛出异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果想要的容量大于MAX_ARRAY_SIZE,则分配Integer.MAX_VALUE,否则分配MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
get和set
public synchronized E get(int index) {
//判断下标是否越界
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
//调用elementData(下标)返回对应元素
return elementData(index);
}
public synchronized E set(int index, E element) {
//判断下标是否越界
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
//oldValue保存返回数组对应下标的元素
E oldValue = elementData(index);
//对应位置赋值
elementData[index] = element;
//返回旧值
return oldValue;
}
//直接返回数组对应下标的元素
E elementData(int index) {
return (E) elementData[index];
}
remove(Object o)
public boolean remove(Object o) {
return removeElement(o);
}
// 删除元素后会把被删除元素后面的所有元素向左移动一个位置
public synchronized boolean removeElement(Object obj) {
// modCount + 1
modCount++;
// 找到第一个出现此元素的下标
int i = indexOf(obj);
if (i >= 0) {
// 如果元素存在则删除
removeElementAt(i);
return true;
}
return false;
}
public int indexOf(Object o) {
// 从下标 0 开始找
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;
}
public synchronized void removeElementAt(int index) {
// modCount + 1
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);
}
// 数量 - 1
elementCount--;
// 最后一个元素设置为 null
elementData[elementCount] = null; /* to let gc do its work */
}
remove(int)
public synchronized E remove(int index) {
//改动次数+1
modCount++;
//下标越界
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
//保存旧值
E oldValue = elementData(index);
//计算要移动元素的个数
int numMoved = elementCount - index - 1;
//如果移动数>0 ,拷贝
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//末尾元素设为空,让gc回收
elementData[--elementCount] = null; // Let gc do its work
//返回旧值
return oldValue;
}
Vector 和ArrayList 的区别
- Vector的方法都是同步的(
Synchronized
),线程安全,而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。 - 当Vector或ArrayList中的元素超过它的初始大小时**,Vector会将它的容量翻倍(未设置容量增量时),而ArrayList扩容为1.5倍**,这样,ArrayList就有利于节约内存空间。
List集合对比
List接口一共有三个实现类,分别是ArrayList、Vector和LinkedLis
ArrayList
是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当在ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。Vector
与ArrayList
一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector
,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢LinkedList
是用链表结构存储数据的,很适合数据的动态插入和删除,但是随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,可以直接操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。vector
是线程同步(Synchronized
)的,所以它也是线程安全的,而Arraylist是线程异步(ASynchronized)的,是不安全的。如果不考虑线程的安全因素,一般用Arraylist效率比较高。- 如果集合中的元素的数目大于目前集合数组的长度时,
vector
增长率为目前数组长度的100%,而arraylist
增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector
比较好
元素操作
如果查找一个指定位置的数据,vector
和arraylist
使用的时间是相同的,都是0(1),这个时候使用vector
和arraylist
都可以,Linkedlist
而查询一个指定位置的数据时花费的时间为0(i)
如果移动一个指定位置的数据,vector
和arraylist
花费的时间为0(n-i),这时候应该使用Linkedlist
,因为它移动一个指定位置的数据所花费的时间为0(1)
原因:
ArrayList
和Vector
是采用数组方式存储数据,允许直接通过索引定位元素,但是插入数据要涉及到数组元素移动 等内存操作,所以查找数据快插入数据慢,Vector
由于使用了synchronized
方法所以性能上比ArrayList
要差,LinkedList
使用双向链表实现存储,可以根据索引在整个链表中的位置进行向前或向后遍历,但是插入数据时只需要操作被删除结点的前后结点即可,所以插入速度较快
时间复杂度 | 查找一个指定位置 | 移动一个指定位置的数据 |
---|---|---|
vector | 0(1) | 0(n-i) |
arraylist | 0(1) | 0(n-i) |
Linkedlist | 0(i) | 0(1) |