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

 

 

posted @ 2020-09-10 11:08  顶风少年  阅读(217)  评论(0编辑  收藏  举报
返回顶部