zoukankan      html  css  js  c++  java
  • 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

     

  • 相关阅读:
    边走边学Nodejs (基础入门篇)
    Android应用打包安装过程具体解释
    ubuntu与centos安装软件的不同点总结
    你好,C++(12)怎样管理多个类型同样性质同样的数据?3.6 数组
    oracle暂时表空间 ORA-01652:无法通过16(在表空间XXX中)扩展 temp 字段
    iOS中sqlite3操作
    sparkSQL1.1入门之二:sparkSQL执行架构
    [NHibernate]视图处理
    [NHibernate]立即加载
    [NHibernate]延迟加载
  • 原文地址:https://www.cnblogs.com/zumengjie/p/13630683.html
Copyright © 2011-2022 走看看