JDK源码之Vector
下文带/**/为源码注释//为个人注释。源代码使用这个颜色
Vector可以设置增量的集合
add(e)扩容增量值得使用
add(idx,e)末尾追加还是任意插入?
set(idx,ele)替换数组元素
get(idx)根据数组下标获取
remove(idx)使用本地方法复制数组
remove(obj)删除第一个匹配的元素
setSize()扩容还是截断?
ArrayList和Vector的区别
Vector可以设置增量的集合
/*存储矢量分量的数组缓冲区。向量的容量是这个数组缓冲区的长度,并且至少足够大来包含向量的所有元素。*/
//存放集合元素的数组
protected Object[] elementData;
/*当矢量的大小大于其容量时,其容量自动增加的数量。如果容量增量小于或等于零,则每次需要增长时,向量的容量就增加一倍。*/
//应该是集合扩容时用到的,它的作用还有待考
protected int capacityIncrement;
//集合的长度,默认是0
protected int elementCount;
/*构造一个空向量,以便其内部数据数组的大小为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);
//创建一个容量10的数组
this.elementData = new Object[initialCapacity];
//增量为0
this.capacityIncrement = capacityIncrement;
}
add(e)扩容增量值得使用
/*将指定的元素追加到此向量的末尾。*/
//加锁了
public synchronized boolean add(E e) {
modCount++;
//第一次添加传入参数为elementCount=0 (0+1)
//第二次添加传入参数为elementCount=1 (1+1)
//第三次添加传入参数为elementCount=2 (2+1)
//第四次添加传入参数为elementCount=3 (3+1)
//第五次添加传入参数为elementCount=4 (4+1)
//第六次添加传入参数为elementCount=5 (5+1)
//第七次添加传入参数为elementCount=6 (6+1)
//第八次添加传入参数为elementCount=7 (7+1)
//第九次添加传入参数为elementCount=8 (8+1)
//第十次添加传入参数为elementCount=9 (9+1)
//第十一次添加传入参数为elementCount=10 (10+1)
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
//集合错误长度这个值是 -2147483649
public static final int MAX_VALUE = 0x7fffffff;
//集合最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/*这实现了ensureCapacity的非同步语义。该类中的同步方法可以在内部调用此方法以确保容量,而不会产生额外同步的开销。*/
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
//判断是否扩容,初始容器是10第11次添加才会触发扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//第11次添加这里传入的值是11
private void grow(int minCapacity) {
// overflow-conscious code
//elementData.length=10
int oldCapacity = elementData.length;
//capacityIncrement>0 false
//newCapactiy=10+10,在这能看出来,如果自己设置了增量
//那么每次扩容则按照增量值为标准
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);
//什么情况下这里才能<0如果没有传递增量,newCapctiy都是oldCapacity的两倍
//如果传递了,不能是0也不能是负数就算是1那么11-11也不<0
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//这里表示如果新的容量大于了阈值这个阈值是Integer.MAX_VALUE - 8;
//到这里newCapacity肯定是正数也就是只有大于MAX_ARRAY_SIZE才是>0
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//最后拷贝数组,ArrayList也是这个方法拷贝的,这次进去看看
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
//这个条件不会满足
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果大于了则是负数其实也是错误的,否则使用最大值MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
//这是Arrays的方法
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
//三个参数是原数组,扩容长度,数组类型
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
//判断数组类型是不是Object如果是直接创建一个Object类型的数组
//如果不是则创建一个指定类型的数组,newInstance后边也是本地方法
//最后调用本地方法System.arraycopy。关于这个方法的参数在ArrayList
//中聊过这里不展开说了
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
add(idx,e)末尾追加还是任意插入?
/*
将指定元素插入到此向量的指定位置。将元素当前的位置(如果有的话)
和后续的元素向右移动(在它们的索引中添加一个)。
*/
public void add(int index, E element) {
insertElementAt(element, index);
}
/*
将指定的对象作为组件插入到这个向量中指定的{@code索引}处。
这个向量中索引大于或等于指定的{@code索引}的每个组件向上
移动,使其索引值比之前的值大1。
*/
//方法加锁了,参数是要添加得元素和下标
public synchronized void insertElementAt(E obj, int index) {
//计数器
modCount++;
//在这里限定了如果下标大于当前集合总长度则报错。
//也就是说index最多只能是集合size,推几行数据
//A:elementCount=0,index可以=0
//B:elementCount=1,index可以=0,1
//C:elementCount=2,index可以=0,1,2
//D:elementCount=3,index可以=0,1,2,3
//E:elementCount=4,index可以=0,1,2,3,4
//F:elementCount=5,index可以=0,1,2,3,4,5
//如果index==elementCount说明是往数组末尾追加元素
//如果index<elementCount是什么情况呢?
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
//这个方法控制数组是否扩容,假设我们数组当前不满足10则不会触发第一次扩容
//跳过这个方法继续看
ensureCapacityHelper(elementCount + 1);
//arraycopy的参数是:源数组,从这个位置开始复制,目标数组,从这个位置开始放,复制几个元素
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
//如果是A情况实参为(设元素类型是Integer):[0,0,0,0,0,0,0,0,0,0],0,[0,0,0,0,0,0,0,0,0,0],1,0
//结果其实是没有对数组进行任何操作的,因为要复制的元素为0个。
//最后执行elementData[0]=obj其实就是在数组0的位置放了元素,这个结果相当于追加
//如果是B情况则可以有两个index分别是0,1先来看1的。
//[8,0,0,0,0,0,0,0,0,0],1,[8,0,0,0,0,0,0,0,0,0],2,0
//在这种情况下还是没有任何操作。最终最后执行elementData[0]=obj
//来看B情况的index=0
//[8,0,0,0,0,0,0,0,0,0],0,[8,0,0,0,0,0,0,0,0,0],1,1
//结果是[8,8,0,0,0,0,0,0,0,0]
//然后在执行elementData[1]=obj,把下标0的替换掉[9,8,0,0,0,0,0,0,0,0]
//这样的结果就是index位置插入原有的往后挪。
//最后看一个C情况它可选的index是0,1,2
如果是2则[8,9,0,0,0,0,0,0,0,0],2,[8,9,0,0,0,0,0,0,0,0],3,0
只要最后一个实参是0则表示要诺的元素就是0个,则没有任何改变。
最后执行elementData[0]=obj结果是[8,9,7,0,0,0,0,0,0,0],追加
如果是1则[8,9,0,0,0,0,0,0,0,0],1,[8,9,0,0,0,0,0,0,0,0],2,1
结果为:[8,9,9,0,0,0,0,0,0,0]执行elementData[1]=obj结果为[8,7,9,0,0,0,0,0,0,0]后挪
如果是0则[8,9,0,0,0,0,0,0,0,0],0,[8,9,0,0,0,0,0,0,0,0],1,2
结果是:[8,8,9,0,0,0,0,0,0,0]执行elementData[0]=obj结果为[7,8,9,0,0,0,0,0,0,0]后挪
set(idx,ele)替换数组元素
/*将向量中指定位置的元素替换为指定元素。*/
//加锁了
public synchronized E set(int index, E element) {
//index不能超过当前集合的长度,也就规定了只能是替换
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
//根据下标获取old值
E oldValue = elementData(index);
//在下标位置设置新值
elementData[index] = element;
//返回old
return oldValue;
}
get(idx)根据数组下标获取
/**返回向量中指定位置的元素。**/
//加锁了
public synchronized E get(int index) {
//index必须小于集合size
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
//返回
return elementData(index);
}
remove(idx)使用本地方法复制数组
/*移除向量中指定位置的元素。将后面的元素向左移动(从它们的索引中减去1)。返回从向量中删除的元素。*/
public synchronized E remove(int index) {
//计数器
modCount++;
//index大于等于size抛出异常
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;
}
设当前集合为[a,b,c,d,e]要删除a则index=0
numMoved=5-0-1=4
arraycopy的参数列表为[a,b,c,d,e],1,[a,b,c,d,e],0,4
结果为[b,c,d,e,e],最后elementData[- -5]=null结果为[b,c,d,e,null]
设当前集合为[a,b,c,d,e]要删除b则index=1
numMoved=5-1-1=3
arraycopy的参数列表为[a,b,c,d,e],2,[a,b,c,d,e],1,3
结果为[a,c,d,e,e],最后elementData[- -5]=null结果为[a,c,d,e,null]
设当前集合为[a,b,c,d,e]要删除e则index=4
numMoved=5-4-1=0
arraycopy的参数列表为[a,b,c,d,e],5,[a,b,c,d,e],4,0
最后一个参数为0表示不移动则这个方法无意义。
最后执行elementData[- -5]=null结果为[a,b,c,d,null]
remove(obj)删除第一个匹配的元素
/*删除此向量中第一次出现的指定元素。如果向量不包含该元素,则该元素不变*/
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);
}
/*
返回指定元素在此向量中第一次出现的索引,从{@code index}向前搜索,或者如果没有找到该元素,返回-1。
*/
//查询元素的下标
public synchronized int indexOf(Object o, int index) {
//如果是null则循环判断null,从0开始
if (o == null) {
for (int i = index ; i < elementCount ; i++)
if (elementData[i]==null)
return i;
} else {
//如果不是null则循环调用元素的equals方法,从0开始
for (int i = index ; i < elementCount ; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
/*
删除指定索引处的组件。在这个向量中,索引大于或等于指定的{@code索引}的
每个组件都向下移动,使其索引值比之前的值小1。这个向量的大小被减少了{@code 1}。
*/
public synchronized void removeElementAt(int index) {
//计数器
modCount++;
//下标越界
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
//下标小于0
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
//下边的逻辑就跟remove(idx)差不多了,调用arraycopy()
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 */
}
setSize()扩容还是截断?
/*
设置这个向量的大小。如果新的大小大于当前大小,新的{@code null}项将被添加到向量的末尾。
如果新大小小于当前大小,索引{@code newSize}和更大的所有组件将被丢弃。
*/
//加锁了
public synchronized void setSize(int newSize) {
modCount++;
//如果新的size大于原来的,则直接选择扩容,扩容的方式上边有解释
if (newSize > elementCount) {
ensureCapacityHelper(newSize);
} else {
//从新的size开始往后所有的值都置为null
for (int i = newSize ; i < elementCount ; i++) {
elementData[i] = null;
}
}
//重新赋值size
elementCount = newSize;
}
ArrayList和Vector的区别
1 容量初始化时机
ArrayList如果使用无参构造器,则集合的容量在第一次添加时扩容为10
Vector如果使用无参构造,Vector会默认设置一个10的容量
2 扩容因子
ArrayList不能手动设置扩容因子,Vector可以通过构造参数设置扩容因子
3 扩容算法
ArrayList扩容的算法是 newsize=oldsize+oldsize/2
Vector扩容的算法是 newsize=oldsize+oldsize或者newsize=oldsize+扩容因子
4 同步和非同步
ArrayList的增删改查均没有加锁
Vector的增删改查均加的有 synchronized
5 更改size
ArrayList不能手动更改size
Vector可以手动更改size