1.Vector前导
1.1继承体系
1.2简单概述
Vector是一个古老的线程安全(内部的核心方法都加了synchronized)
的容器,在JDK1.0时就已经存在,到如今已经很少使用。基本结构与ArrayList类似,可以认为是线程安全版本的ArrayList,但因为Vector效率低下,所以在多线程下使用的基本都是 CopyOnWriteArrayList() 其效率更高。
2.核心结构
2.1属性
//底层存储数据的数组
protected Object[] elementData;
//数组中的元素个数
protected int elementCount;
/*
* 扩容时指定扩容的容量(>0),即扩容后的容量为 oldCap + capacityIncrement
* 若没有指定,那么默认扩容为原容量的2倍,即扩容后的容量为 2 * oldCap
*/
protected int capacityIncrement;
2.2构造器
调用无参构造器时,默认初始化容量为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);
}
3.核心方法
3.1add()
/*
* 加锁方法,线程安全。
*/
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;
}
3.2*grow()/newCapacity()
/*
* 调用了重载的带参数的grow(int minCapacity)方法
*/
private Object[] grow() {
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()
扩容大致流程
这里只关注普通情况,因为一般一个集合中的元素个数不会达到INF级别。
- 当 capacityIncrement大于0 时,
newCap = oldCap + capacityIncrement
- 否则 newCap = oldCap * 2
/*
* 计算出最终扩容后的数组长度。
*/
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.youkuaiyun.com/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;
}
3.3remove()
/*
* (同步方法) 将指定索引位置的元素置为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。
elementData[--elementCount] = null; // Let gc do its work
//返回旧的value。
return oldValue;
}
// ||
// ||
// \/
/*
* 获取指定索引位置的元素
*/
E elementData(int index) {
return (E) elementData[index];
}
3.4set()
/*
* (同步方法) 修改指定索引位置的元素
*/
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;
}
3.5size()/isEmpty()
//返回elementCount
public synchronized int size() {
return elementCount;
}
//判断elementCount是否为0
public synchronized boolean isEmpty() {
return elementCount == 0;
}
3.6get()
/*
* 同步方法 获取指定索引位置的元素
*/
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];
}
4.总结
-
Vector是非懒加载的,调用无参构造器时默认会初始化一个长度为
10
的数组。 -
默认的扩容为原长度的2倍(ArrayList为1.5倍)(未给capacityIncrement赋值时)。
-
Vector的方法基本都是加了
synchronized
的,是一个线程安全的容器。
由于Vector现在基本不用,所以这里只讲解了其核心的方法,下期我们简单分析一下Vector的子类-Stack的源码。