描述
Vector是一个基于数组实现的集合容器类,几乎和ArrayList的实现思想相同,但是保证了线程操作的安全性。
构造器
Vector的构造方法总共有4个。
- 无参构造器
// 这个构造器会构造一个初始长度为10,增长容量为0的向量对象
public Vector() {
// 直接调用预期长度的构造器,#2
this(10);
}
- 预设初始容量的构造器
public Vector(int initialCapacity) {
// 调用2个参数的构造器
this(initialCapacity, 0);
}
- 预设初始容量和容量增长数量的构造器
public Vector(int initialCapacity, int capacityIncrement) {
super();
// 对传入的预期容量做合规性检查
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
// 直接根据预期长度初始化保存数据的数组
this.elementData = new Object[initialCapacity];
// 初始化容量扩容增长的数值
this.capacityIncrement = capacityIncrement;
}
- 从已有集合创建的构造器
public Vector(Collection<? extends E> c) {
// 将传入的集合转化为数组,直接赋值给存储的数组
elementData = c.toArray();
// 记录初始化时的集合元素个数
elementCount = elementData.length;
// 健壮性代码
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
add
add方法有2个重载,这个是跟ArrayList一样的
- 在集合尾部添加新对象
// 注意,这个方法是用 synchronized 关键词修饰来保证线程安全的
public synchronized boolean add(E e) {
modCount++;
// 有需要的话进行扩容,预期的扩容容量最小值就是当前元素个数+1
ensureCapacityHelper(elementCount + 1);
// 将当前的集合计数+1
// 在最后一个位置放入新元素对象
elementData[elementCount++] = e;
return true;
}
- 在指定索引的位置插入新元素
public void add(int index, E element) {
insertElementAt(element, index);
}
public synchronized void insertElementAt(E obj, int index) {
modCount++;
// 保证插入的索引号没有超过当前已有元素的最大值
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
// 有必要的话先进行扩容,扩容的最小期望容量为当前集合长度+1
ensureCapacityHelper(elementCount + 1);
// 将当前数组中的元素从index位置开始,往后移动一位
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
// 将插入的新对象放置在index索引对应的位置
elementData[index] = obj;
// 更新当前集合的新容量
elementCount++;
}
扩容
private void ensureCapacityHelper(int minCapacity) {
// 只有当期望的新容量比当前数组已分配的容量大的时候,才进行真正的扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 获取扩容前的数组的容量
int oldCapacity = elementData.length;
// 如果设置了每次扩容的增长容量,也就是在构造器中传入的参数,那么就在原来容量的基础上新增capacityIncrement个数的新容量。
// 如果没有设置,那么新容量就是老容量的2倍。
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);
}
remove
remove方法同样有2个重载。
- 删除指定索引位置的元素
// 同样使用 synchronized 关键词保证线程安全
public synchronized E remove(int index) {
modCount++;
// 保证删除的索引号不会超过当前已有元素的最大值
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 获取到删除之前的元素
E oldValue = elementData(index);
// 计算删除元素以后需要移动的元素的个数,需要移动的元素个数为,
// 从index开始到最后一个元素的所有元素的个数
int numMoved = elementCount - index - 1;
// 只有当有需要移动的元素才操作数组进行移动
if (numMoved > 0)
// 将从index+1开始的所有元素都移动到index位置
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将集合长度减一
// 将当前数组中的最后一个元素指向null,等待GC回收
elementData[--elementCount] = null; // Let gc do its work
// 返回被删除的元素
return oldValue;
}
- 删除指定的对象
public boolean remove(Object o) {
return removeElement(o);
}
public synchronized boolean removeElement(Object obj) {
modCount++;
// 先找到该对象是在数组中的哪个索引号对应的位置
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)
// 注意:这里返回的是第一个为Null的对象的索引号
return i;
} else {
for (int i = index ; i < elementCount ; i++)
if (o.equals(elementData[i]))
// 注意:这里返回的是第一个和传入对象相等的对象的索引号
return i;
}
return -1;
}
// 这个方法整体上和#1是相同的
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 */
}
get
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
// 直接从数组中找到对应索引号的对象,强制转化为目标类型之后返回
E elementData(int index) {
return (E) elementData[index];
}
set
public synchronized E set(int index, E element) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 记录原来的值
E oldValue = elementData(index);
// 将新设置的对象赋值给当前index位置
elementData[index] = element;
// 返回原来的元素对象
return oldValue;
}
清空
public void clear() {
removeAllElements();
}
public synchronized void removeAllElements() {
modCount++;
// 循环的将数组中的所有元素都指向null,让虚拟机在下一次GC的时候将这些空间收回
for (int i = 0; i < elementCount; i++)
elementData[i] = null;
// 将集合长度设置为0
elementCount = 0;
}
其他
和ArrayList比起来,绝大多数的操作和实现都是类似的,最大的区别有2个。
Vector通过在主要的操作方法上使用synchronized关键词,来保证线程的安全性Vector在扩容的时候的策略跟ArrayList不同,ArrayList扩容的时候为扩大到原来的1.5倍,Vector直接扩大1倍