Vector源码分析
继承实现
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
AbstractList
继承了AbstractCollection
抽象类,实现了最基本的集合的方法。大多数的集合都会继承此类或此类的派生类。List
接口定义了一个集合该有的一系列方法。RandomAccess
,用于快速随机访问。空接口。Cloneable
,实现了克隆方法。Serializable
,实现了序列化方法。
重要属性
// 元素个数
protected int elementCount;
// 存储数据的数组
protected Object[] elementData;
// 扩容增量
protected int capacityIncrement;
// 一开始数组的默认大小
private static final int DEFAULT_SIZE = 10;
- 由此可见,Vector的核心实现就是顺序存储的线性表——数组。
构造方法
public Vector() {
this(DEFAULT_SIZE, 0);
}
public Vector(int capacity) {
this(capacity, 0);
}
public Vector(int capacity, int capacityIncrement) {
if (capacity < 0) {
throw new IllegalArgumentException("capacity < 0: " + capacity);
}
elementData = newElementArray(capacity);
elementCount = 0;
this.capacityIncrement = capacityIncrement;
}
public Vector(Collection<? extends E> collection) {
this(collection.size(), 0);
Iterator<? extends E> it = collection.iterator();
while (it.hasNext()) {
elementData[elementCount++] = it.next();
}
}
- 前两个构造方法都是继续调用的第3个构造方法。
- 一般来说,第一个构造方法是调用者最常用的。不需要传任何参数。因为如果容量满了,
Vector
会自动扩容。 - 因此,实际上第三个构造方法才是真正执行最多的!
- 第四个构造方法,其实现一目了然。用迭代器将传来的集合中的元素一个一个赋值给
Vector
的数组elementData
。 - 第三个构造的
newElementArray
方法,实际上就是创建一个数组,返回引用。然后保存容量增量。如下:
private E[] newElementArray(int size) {
return (E[]) new Object[size];
}
功能方法
add&insertElementAt
@Override
public void add(int location, E object) {
insertElementAt(object, location);
}
@Override
public synchronized boolean add(E object) {
if (elementCount == elementData.length) {
growByOne();
}
elementData[elementCount++] = object;
modCount++;
return true;
}
- 两个
add
方法 - 第2个
add
方法是最常用的。 - 第2个
add
方法。先判断容量是否已满,如果满了就会调用growByOne
方法。
/**
* 确定扩容增量,进行数组的扩容
*/
private void growByOne() {
// 1.先判断容量增量是否<=0,如果无增量再判断数组的长度是否==0.
// 2.如果连数组长度都等于0了,就说明这就是个空数组。给增量给1就行了。
// 3.如果容量增量大于0,说明已经指定了此属性。到这里,已经确定增量多少合适!
// 4.然后新建数组,新数组长度 = 原数组长度 + 合适的容量增量
// 5.使用System.arraycopy将 原数组 数据 拷贝到 新数组 中
// 6.最后将新数组引用赋值给 属性【elementData】
int adding = 0;
if (capacityIncrement <= 0) {
if ((adding = elementData.length) == 0) {
adding = 1;
}
} else {
adding = capacityIncrement;
}
E[] newData = newElementArray(elementData.length + adding);
System.arraycopy(elementData, 0, newData, 0, elementCount);
elementData = newData;
}
- 据上述,可以知晓。
growByOne
就是进行数组扩容。elementData
已经是扩容后的新数组了。 - 然后直接添加元素到数组中。
modCount
就是个计数器,对于数组的修改,它都会++
。这个不重要!- 而第1个
add
方法直接调用insertElementAt
,见名知意。在指定位置插入元素。具体如下:
// 在指定位置插入元素
public synchronized void insertElementAt(E object, int location) {
// 1.首先确保location在有效范围内
if (location >= 0 && location <= elementCount) {
// 2.如果容量已满,就进行数组扩容
if (elementCount == elementData.length) {
growByOne();
}
// 3.计算数组从location位置开始,后面的多少个元素需要后移一位
int count = elementCount - location;
if (count > 0) {
// 4.进行数组的部分元素后移
System.arraycopy(elementData, location, elementData,
location + 1, count);
}
// 5.指定位置插入元素
elementData[location] = object;
// 6.元素个数++
elementCount++;
modCount++;
} else {
throw arrayIndexOutOfBoundsException(location, elementCount);
}
}
小结
- 从
add
方法可以看出,插入元素需要将该位置以后的所有元素后移。效率不高!删除也应该如此。 - 查询效率应该不错!因为是顺序存储。
Vector
的add
方法加了synchronized
,线程安全。但因为有锁,因此效率降低!
addAll
@Override
public synchronized boolean addAll(Collection<? extends E> collection) {
return addAll(elementCount, collection);
}
@Override
public synchronized boolean addAll(int location, Collection<? extends E> collection) {
// 1.确保location的有效范围
if (location >= 0 && location <= elementCount) {
int size = collection.size();
// 2.确保添加进来的集合不为null
if (size == 0) {
return false;
}
// 3.elementData.length - elementCount表示数组还剩多少个空余
// size - (elementData.length - elementCount)表示加上添加进来的集合还需要多少个空余
int required = size - (elementData.length - elementCount);
if (required > 0) {
// 4.如果确实需要空余,那么就进行数组扩容
growBy(required);
}
// 5.需要让多少个元素后移
int count = elementCount - location;
if (count > 0) {
// 6.进行后移
System.arraycopy(elementData, location, elementData, location
+ size, count);
}
Iterator<? extends E> it = collection.iterator();
// 7.迭代器遍历,赋值到数组中间后移出来空余的部分
while (it.hasNext()) {
elementData[location++] = it.next();
}
// 8.元素增加!
elementCount += size;
modCount++;
return true;
}
throw arrayIndexOutOfBoundsException(location, elementCount);
}
addAll
方法就是将数组从location
开始到最后往后移动,移动collection.size()
个。然后遍历把collection
放进来。- 也是锁方法,安全,效率低!
addElement
public synchronized void addElement(E object) {
// 1.如果容量已满,就进行数组扩容
if (elementCount == elementData.length) {
growByOne();
}
// 2.在数组最后添加元素
elementData[elementCount++] = object;
modCount++;
}
- 锁方法,安全,效率低!
capacity
// capacity容量的意思
// 返回这个数组的容量,但记住不是元素的个数
// 而是数组的容量。容量 >= 元素个数。
public synchronized int capacity() {
return elementData.length;
}
- 锁方法,安全,效率低!
- 注意容量和长度的区别。
clear&removeAllElements
@Override
public void clear() {
removeAllElements();
}
public synchronized void removeAllElements() {
// 1.将所有的元素都置为null
// 2.将元素个数设为0
for (int i = 0; i < elementCount; i++) {
elementData[i] = null;
}
modCount++;
elementCount = 0;
}
- 数组的
clear
大多数都是这么做的,元素置为null
,再将计数elementCount = 0
.
clone
@Override
@SuppressWarnings("unchecked")
public synchronized Object clone() {
try {
// 1.clone Vector的基本属性
Vector<E> vector = (Vector<E>) super.clone();
// 2.再将重要属性:数组,克隆一份
vector.elementData = elementData.clone();
// 3.clone一份相同的对象!
return vector;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
index&contains
@Override
public boolean contains(Object object) {
return indexOf(object, 0) != -1;
}
@Override
public int indexOf(Object object) {
return indexOf(object, 0);
}
// 查找object元素从location位置开始到最后,第一次出现的位置
// 要查的object可以为null
public synchronized int indexOf(Object object, int location) {
if (object != null) {
// 要查的数据不为null
// 从location位置开始到最后,一个一个比较
for (int i = location; i < elementCount; i++) {
if (object.equals(elementData[i])) {
return i;
}
}
} else {
// 要查的数据为null
// 从location位置开始到最后,一个一个比较
for (int i = location; i < elementCount; i++) {
if (elementData[i] == null) {
return i;
}
}
}
return -1;
}
contains
是查询集合中是否包含此元素。indexOf
是为了查询元素正序第一次出现的位置。- 因此,
contains
可以利用indexOf
满足需求。 indexOf
的本质就是从头到尾挨个比较!
containsAll
@Override
public synchronized boolean containsAll(Collection<?> collection) {
return super.containsAll(collection);
}
containsAll
直接调用父类的containsAll
方法,但不是AbstractList
抽象类的,而是AbstractCollection
抽象类的。
// AbstractCollection.java
public boolean containsAll(Collection<?> collection) {
// 1.迭代器遍历collection的每一个元素
// 2.只要collection集合中有一个元素不在当前集合中,就返回false
Iterator<?> it = collection.iterator();
while (it.hasNext()) {
if (!contains(it.next())) {
return false;
}
}
return true;
}
copyInto
public synchronized void copyInto(Object[] elements) {
// 将当前集合中的元素全部拷贝到elements数组中
// 如果elements中有数据,会进行覆盖,覆盖elementCount个
System.arraycopy(elementData, 0, elements, 0, elementCount);
}
- 将当前集合中的元素全部拷贝到另一个数组中。
elementAt
@SuppressWarnings("unchecked")
public synchronized E elementAt(int location) {
if (location < elementCount) {
// 返回指定位置的元素
return (E) elementData[location];
}
throw arrayIndexOutOfBoundsException(location, elementCount);
}
elements
// Enumeration(枚举)接口的作用和Iterator类似,只提供了遍历Vector和HashTable类型集合元素的功能,
// 不支持元素的移除操作。
public Enumeration<E> elements() {
return new Enumeration<E>() {
int pos = 0;
// 是否有下个元素
public boolean hasMoreElements() {
return pos < elementCount;
}
// 返回下个元素
@SuppressWarnings("unchecked")
public E nextElement() {
synchronized (Vector.this) { // 锁
if (pos < elementCount) {
// 范围内,返回元素数据
return (E) elementData[pos++];
}
}
throw new NoSuchElementException();
}
};
}
Enumeration
用的不多了,因为和Iterator
重叠的地方太多。且不如Iterator
功能多。这个当作了解即可。
ensureCapacity&grow
// 此方法的作用是:
// 你给我指定想要扩容的大小
// 我根据现有数组的使用情况,计算比较合适的扩容的大小
// 两者比较,确定最终合适的大小
// 然后调用grow进行扩容,扩容后的数组直接赋值给属性elementData
public synchronized void ensureCapacity(int minimumCapacity) {
if (elementData.length < minimumCapacity) {
// 如果容量增量等于0,就选择数组长度,否则选择增量
// 选择完毕后的增量,再加上数组长度 为 合适的增量
int next = (capacityIncrement <= 0 ? elementData.length
: capacityIncrement)
+ elementData.length;
// 如果传来的指定的增量大于计算的合适的增量,就用指定的增量
// 否则就用计算好的合适的增量
grow(minimumCapacity > next ? minimumCapacity : next);
}
}
private void grow(int newCapacity) {
// 新建指定大小的数组
E[] newData = newElementArray(newCapacity);
// 拷贝所有数据到新数组中
System.arraycopy(elementData, 0, newData, 0, elementCount);
// 将新数组赋值给属性
elementData = newData;
}
growBy
// 此方法的作用是:
// 你传来一个你想要的数组的容量
// 我经过一系列计算最终确定增量多少,然后新建数组,拷贝数据
private void growBy(int required) {
int adding = 0; // 数组的容量需要增加多少
if (capacityIncrement <= 0) { // 如果增量为0
if ((adding = elementData.length) == 0) { // 数组也是空的
adding = required; // 增加为指定的量
}
// 如果数组不为空,让adding一直加数组.length
// 直到adding >= 指定的量
while (adding < required) {
adding += adding;
}
} else { // 如果增量不为0
// 反正也是一些计算
adding = (required / capacityIncrement) * capacityIncrement;
if (adding < required) {
adding += capacityIncrement;
}
}
// 最终确定数组需要的容量
// 新建数组
// 拷贝数据
// 赋值属性
E[] newData = newElementArray(elementData.length + adding);
System.arraycopy(elementData, 0, newData, 0, elementCount);
elementData = newData;
}
- 与
growByOne
类似,外界很少调用。都是内部调的。理解意思即可。
isEmpty
@Override
public synchronized boolean isEmpty() {
return elementCount == 0;
}
- isEmpty,比较的是元素个数而不是数组长度。这一点一定要注意!
lastElement
@SuppressWarnings("unchecked")
public synchronized E lastElement() {
try {
return (E) elementData[elementCount - 1];
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
elementCount
是元素个数,索引从0
开始。所以最多到elementCount-1
。
lastIndexOf
@Override
public synchronized int lastIndexOf(Object object) {
return lastIndexOf(object, elementCount - 1);
}
public synchronized int lastIndexOf(Object object, int location) {
// 1.确保location的有效范围
if (location < elementCount) {
if (object != null) { // 如果要查询的元素不为null
for (int i = location; i >= 0; i--) { // 从后往前遍历比较
if (object.equals(elementData[i])) {
return i; // 找到就返回索引位置
}
}
} else { // 如果要查询的元素为null,null也可以作为一种元素存储
for (int i = location; i >= 0; i--) { // 从后往前遍历比较
if (elementData[i] == null) {
return i; // 找到就返回索引位置
}
}
}
return -1;
}
throw arrayIndexOutOfBoundsException(location, elementCount);
}
indexOf
正序查找,lastIndexOf
逆序查找。但查找内容一样。
remove&removeElement&removeElementAt
@SuppressWarnings("unchecked")
@Override
public synchronized E remove(int location) {
if (location < elementCount) { // 确保location的有效范围
E result = (E) elementData[location]; // location位置上的数据
elementCount--; // 有效元素个数--
int size = elementCount - location; // 需要偏移的元素的个数
if (size > 0) {
// 进行偏移,删除一个,所以往前偏移
System.arraycopy(elementData, location + 1, elementData,
location, size);
}
elementData[elementCount] = null; // 最后一位置为null
modCount++;
return result;
}
throw arrayIndexOutOfBoundsException(location, elementCount);
}
@Override
public boolean remove(Object object) {
return removeElement(object);
}
public synchronized boolean removeElement(Object object) {
int index;
if ((index = indexOf(object, 0)) == -1) { // 用正序indexOf查找到元素的索引
return false;
}
// 调用方法进行删除
removeElementAt(index);
return true;
}
// 删除指定位置上的元素
public synchronized void removeElementAt(int location) {
if (location >= 0 && location < elementCount) { // 确保location的有效范围
elementCount--; // 有效元素个数--
int size = elementCount - location; // 需要偏移的元素的个数
if (size > 0) {
// 进行偏移,删除一个,所以往前偏移
System.arraycopy(elementData, location + 1, elementData,
location, size);
}
elementData[elementCount] = null; // 最后一位置为null
modCount++;
} else {
throw arrayIndexOutOfBoundsException(location, elementCount);
}
}
- 关键方法
remove(int location)
和removeElementAt(int location)
方法内容何其相似。甚至注释都不用改!
removeRange
@Override
protected void removeRange(int start, int end) {
// 确保范围的有效性
if (start >= 0 && start <= end && end <= elementCount) {
if (start == end) { // 开始和结束相等无意义
return;
}
if (end != elementCount) { // end最多可以到达elementCount - 1
// 拷贝数据从end到最后,拷贝到start开始,一共拷贝elementCount-end个
System.arraycopy(elementData, end, elementData, start,
elementCount - end);
// 设:elementCount = 10, start = 3, end = 5
// newCount = 8
// 8 - 10 fill null ==> 8, 9 设为null
int newCount = elementCount - (end - start);
Arrays.fill(elementData, newCount, elementCount, null);
elementCount = newCount;
} else { // 如果end等于elementCount,这里做容错处理,将数组从start开始往后的所有元素都填充为null
Arrays.fill(elementData, start, elementCount, null);
elementCount = start;
}
modCount++;
} else {
throw new IndexOutOfBoundsException();
}
}
- 范围删除,再
arraycopy
的时候就已经删除了。 - 后面是将最后多余的元素置为
null
retainAll
@Override
public synchronized boolean retainAll(Collection<?> collection) {
return super.retainAll(collection);
}
// AbstractCollection.java
public boolean retainAll(Collection<?> collection) {
boolean result = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
// 当前集合中如果有collection集合不包含的元素,那么就删除
if (!collection.contains(it.next())) {
it.remove();
result = true;
}
}
return result;
}
- 一样的
retainAll
调用的是父类的父类的retainAll
方法 - 取交集,当前集合中只能存在
collection
集合中包含的,collection
集合不包含的元素。当前集合要全部删除。
set
@SuppressWarnings("unchecked")
@Override
public synchronized E set(int location, E object) {
if (location < elementCount) { // 确保location的有效范围
E result = (E) elementData[location]; // location位置上的数据
elementData[location] = object; // 覆盖location位置上的数据
return result;
}
throw arrayIndexOutOfBoundsException(location, elementCount);
}
setElementAt
public synchronized void setElementAt(E object, int location) {
if (location < elementCount) { // 确保location的有效范围
elementData[location] = object; // 设置指定位置上的数据
} else {
throw arrayIndexOutOfBoundsException(location, elementCount);
}
}
setSize&size
public synchronized void setSize(int length) {
// 如果指定长度和现有元素个数相同,就没必要设置了
if (length == elementCount) {
return;
}
// 进行扩容量的判定,判定完进行扩容
ensureCapacity(length);
if (elementCount > length) {
// 如果元素个数大于数组长度,就将剩余的数组元素填充为null
Arrays.fill(elementData, length, elementCount, null);
}
elementCount = length; // 刷新元素个数
modCount++;
}
@Override
public synchronized int size() {
return elementCount;
}
setSize,
设置集合长度- 不能想设置多少就设置多少,所以关键方法
ensureCapacity
会进行计算和判定。 - 判定完再扩容。
总结
Vector
中的方法基本过了一遍,跟ArrayList
很类似。毕竟都是以数组为核心存储数据。Vector
也经常和ArrayList
进行比较,比较结果:Vector
安全,但效率低!ArrayList
不安全,但效率高!ArrayList
就是为了解决Vector
效率低而出的产物。