将分析以下内容
- 字段
- 构造函数
- 扩容
- 插入和删除导致的数组大幅度移动
1.首先来看一下Vector里面的属性
这个就是用来存储元素的数组
protected Object[] elementData;
这个是数组已使用的长度
protected int elementCount;
这个是每次扩容时增加的长度,不同于ArrayList,Vector可以在初始化时指定每次扩容时的长度
protected int capacityIncrement;
2.接下来看一下Vector里面的构造函数
这个参数时无参的,它会默认给我们一个初始容量为10,可以看到还需要调用另一个构造函数
public Vector() {
this(10);
}
这个构造函数给了我们默认的扩容长度增长值,可以看到还需要调用另一个构造函数
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
这个函数指定了初始容量和扩容增长长度,完成对各属性的初始化
public Vector(int initialCapacity, int capacityIncrement) {
super();
//初始容量<0直接抛异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
//初始化数组以及容量增长
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
3.接下来看一下Vector的增删
Vector在线程不安全的地方使用了synchronized修饰,因此Vector是线程安全的
public synchronized boolean add(E e) {
modCount++;//数组修改的次数,该变量在AbstractList定义,不研究
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
在添加元素前,需要调用ensureCapacityHelper()方法,并传递了当前已使用数组长度+1作为最小申请容量
该方法用于判断是否执行扩容,判断条件就是最小申请容量是否大于数组总长度
private void ensureCapacityHelper(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow()方法时执行扩容的具体逻辑
1.计算新容量
- 如果指定了每次扩容增长长度,新容量=旧容量+每次扩容增长长度
- 如果没有指定,新容量=2 * 旧容量
2.判断新容量大小是否小于最小申请容量(当你需要添加多个元素,而你的每次扩容增长长度又设置的太小)
3.新容量大于最大申请容量时,使用Integer.MAX_VALUE(这里直接看一下,一般不会这么大)
//数组分配的最大容量,没人的数组想要这么大的吧,很占内存的
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
//1.计算新容量
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
//2.判断新容量大小是否小于最小申请容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//新容量大于最大申请容量时,使用Integer.MAX_VALUE
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
下面我们来用代码测试一下
public class Test {
public static void main(String[] args) {
Vector vector = new Vector(1, 2);
System.out.println("初始容量:" + vector.capacity());
vector.add("1");
System.out.println("添加1个元素后的容量:" + vector.capacity());
vector.add("2");
System.out.println("添加2个元素后的容量:" + vector.capacity());
vector.add("3");
vector.add("4");
System.out.println("添加4个元素后的容量:" + vector.capacity());
}
}
这里我们了解了Vector是如何进行扩容的,接下来我们看一看Vector如何进行插入
public void add(int index, E element) {
insertElementAt(element, index);
}
传递了元素和索引,我们来看看insertElementAt()是怎么插入元素的
- 判断角标越界没毛病
- 判断容量,前面已经讲了,没毛病
- 调用System.arraycopy()将数组从索引处整个往后挪
- 在索引处设置新的值
public synchronized void insertElementAt(E obj, int index) {
modCount++; //数组修改的次数,该变量在AbstractList定义,不研究
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
下面是System.arraycopy()方法的图解
下面我们来看看Vector如何进行删除
看到下面是不是绝对和插入非常相似
public synchronized E remove(int index) {
modCount++;//数组修改的次数,该变量在AbstractList定义,不研究
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
所以这里也就不再介绍了,至于查询和修改,查询直接返回数组指定索引的值,修改直接修改指定索引的值,没什么说的