Vector源码解析
简介
Vector 是一个古老的 线程安全(内部的核心方法都加了synchronized)
的容器,在 JDK1.0 时就已经存在,到如今已经很少使用。
基本结构与 ArrayList 类似,可以认为是线程安全版本的 ArrayList,但因为 Vector 效率低下,所以在多线程下使用的基本都是 CopyOnWriteArrayList,其效率更高。
Vector 实现了 List 接口,是顺序容器,即元素存存放的数据与放进去的顺序相同,允许存入 null 元素。底层通过数组实现。
继承体系
Vector 继承了 AbstractList,实现了 List,RandomAccess,Cloneable,Serializable 接口,因此 Vector 支持快速随机访问,可以被克隆,支持序列化。
源码解析
属性
// 底层存储数据的数组
// 注意:访问修饰符有所不同,Vector用protected修饰,而ArrayList用private修饰。
// JavaSE中:private变量只能被当前类的方法访问,而protected可以被同一包中的所有类和其他包的子类访问
protected Object[] elementData;
// 数组中的元素个数
protected int elementCount;
/*
* 扩容时指定扩容的容量(>0),即扩容后的容量为 oldCap + capacityIncrement
* 若没有指定,那么默认扩容为原容量的2倍,即扩容后的容量为 2 * oldCap
*/
protected int capacityIncrement;
构造方法
- 无参和单参都是调用的是双参构造方法,默认初始化容量为
10
。
// ----------------------------构造器1----------------------------------
/*
* 传入初始容量和扩容数量
* 这里跟ArrayList不同的是,Vector是非懒加载的,调用构造器就会初始化数组
*/
public Vector(int initialCapacity, int capacityIncrement) {
// 判断初始容量是否合法
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
// 直接构造数组赋值给内部的elementData
this.elementData = new Object[initialCapacity];
// 传入的扩容数量赋值给capacityIncrement
this.capacityIncrement = capacityIncrement;
}
// ----------------------------构造器2-----------------------------------
/*
* 调用是构造器1,只是扩容数量传入的是0,即扩容时默认扩容为原长度的2倍。
*/
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
// ----------------------------构造器3-----------------------------------
/*
* 底层调用构造器2. 默认初始化长度为10. capacityIncrement为0
*/
public Vector() {
this(10);
}
// ----------------------------构造器4-----------------------------------
/*
* 参数为集合类型的构造函数
*/
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
// 将参数集合c 中的数据拷贝到elementData
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
add(E e)
- 添加元素至数组末尾。
/*
* 加锁方法,线程安全
*/
public synchronized boolean add(E e) {
modCount++;
add(e, elementData, elementCount);
return true;
}
||
||
\/
/*
* @param e 添加的元素
* @param elementData 内部数组
@param s 元素个数
*/
private void add(E e, Object[] elementData, int s) {
// 判断元素个数等于数组长度,下面是扩容的逻辑
if (s == elementData.length)
elementData = grow();
// s就是待插入的位置,将e放到集合中
elementData[s] = e;
// 元素个数+1
elementCount = s + 1;
}
grow()
- 扩容,拷贝出一个新数组。
/*
* 调用了重载的带参数的grow(int minCapacity)方法
*/
private Object[] grow() {
// 传入elementCount + 1
return grow(elementCount + 1);
}
||
||
\/
/*
* 1.首先调用newCapacity()方法,计算出扩容后数组的容量x
* 2.然后调用Arrays.copyOf()方法重新创建一个长度为x的数组,然后将原数组中的数据拷贝过去。完成扩容操作
*
* @param minCapacity:当前数组需要存放的元素的个数(即所需要的长度)
*/
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
newCapacity(int minCapacity)
- 真正计算扩容后数组长度的方法。
/*
* 计算出最终扩容后的数组长度
*/
private int newCapacity(int minCapacity) {
// 获取当前数组长度
int oldCapacity = elementData.length;
/*
* 这里就是计算扩容后的长度的核心。
* 1.首先判断capacityIncrement是否大于0,如果大于0,那么这次扩容的长度为oldCapacity + capacityIncrement
* 2.否则,扩容后的长度为 oldCapacity * 2
*/
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
// 判断按照上面的计算后是否满足当前所需要的长度,不满足的话进入if语句
if (newCapacity - minCapacity <= 0) {
// 溢出 直接抛错
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 没有溢出,直接将当前所需要的长度返回
return minCapacity;
}
/*
* MAX_ARRAY_SIZE = INF - 8
* 这里去判断newCapacity是否溢出或者newCapacity大于MAX_ARRAY_SIZE
* 并且没有溢出,此时直接分配INF。
* 这里的处理ArrayList相同,参考[https://blog.csdn.net/qq_46312987/article/details/122096262]
*/
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
||
||
\/
private static int hugeCapacity(int minCapacity) {
// 溢出 直接抛OOM
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
// minCapacity > MAX_ARRAY_SIZE 并且没有溢出(上面if判断),直接分配INF
Integer.MAX_VALUE :
// 否则分配MAX_ARRAY_SIZE
MAX_ARRAY_SIZE;
}
set(int index, E element)
- 修改指定索引位置的元素。
/*
* (同步方法)修改指定索引位置的元素
*/
public synchronized E set(int index, E element) {
// 判断索引是否越界
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 获取index位置的元素
E oldValue = elementData(index);
// 替换
elementData[index] = element;
// 返回原元素
return oldValue;
}
get(int index)
- 获取指定索引位置的元素。
/*
* (同步方法)获取指定索引位置的元素
*/
public synchronized E get(int index) {
// 索引越界判断
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 直接返回index位置的元素。
return elementData(index);
}
||
||
\/
/*
* 获取指定索引位置的元素
*/
E elementData(int index) {
return (E) elementData[index];
}
remove(int index)
- 删除指定索引位置的元素。
/*
* (同步方法)将指定索引位置的元素置为null
*/
public synchronized E remove(int index) {
modCount++;
// 越界判断
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 获取index位置的元素
E oldValue = elementData(index);
// 这里计算出当前准备删除的元素后有多少个元素
int numMoved = elementCount - index - 1;
// 调用System.arraycopy变向的将index位置后面的所有元素往前移动一位
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将最后一个位置置为null
// 获取完下标后 elementCount值自减1
elementData[--elementCount] = null; // Let gc do its work
// 返回旧的value
return oldValue;
}
||
||
\/
/*
* 获取指定索引位置的元素
*/
E elementData(int index) {
return (E) elementData[index];
}
其它方法
// 设置Vector数组的大小
public synchronized void setSize(int newSize) {
// 修改次数++
modCount++;
// 判断设置的数组大小是否大于Vector中有存储的效元素的个数
// 若 newSize > Vector中有存储的效元素的个数,则调整Vector的大小
if (newSize > elementCount) {
// 调用判断是否扩容的方法,如果需要扩容则该方法内部调用扩容方法grow()
ensureCapacityHelper(newSize);
} else {
// 如果上述判断不成立,则将newSize位置之后开始的元素都设置为null
for (int i = newSize ; i < elementCount ; i++) {
elementData[i] = null;
}
}
// 更新有效元素个数
elementCount = newSize;
}
// 获取Vector的当前容量
public synchronized int capacity() {
return elementData.length;
}
// 判断elementCount是否为0
public synchronized boolean isEmpty() {
return elementCount == 0;
}
// 获取Vector里面的有效元素个数
public synchronized int size() {
return elementCount;
}
// 判断Vecotor中是否包含元素 o
public boolean contains(Object o) {
return indexOf(o, 0) >= 0;
}
// 获取Vector数组中第一次出现对象o的下标,如果不存在,那么返回-1
public int indexOf(Object o) {
return indexOf(o, 0);
}
// 返回从index出开始第一次出现对象o的下标,如果不存在,那么返回-1
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;
}
总结
- Vector 是非懒加载的,调用无参构造器时默认会初始化一个长度为
10
的数组。 - 默认的扩容为原长度的 2 倍 (ArrayList 为 1.5 倍)(未给 capacityIncrement 赋值时)。
- Vector 的方法基本都是加了
synchronized
的,是一个线程安全
的容器。
参考文章
- shstart7_Vector源码解析
- 兴趣使然的草帽路飞_JDK集合源码之Vector解析