Vector源码剖析(看不懂直播写检讨)

将分析以下内容

  • 字段
  • 构造函数
  • 扩容
  • 插入和删除导致的数组大幅度移动

1.首先来看一下Vector里面的属性

这个就是用来存储元素的数组

protected Object[] elementData;

这个是数组已使用的长度

protected int elementCount;

这个是每次扩容时增加的长度,不同于ArrayList,Vector可以在初始化时指定每次扩容时的长度

protected int capacityIncrement;

2.接下来看一下Vector里面的构造函数

这个参数时无参的,它会默认给我们一个初始容量为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);  
    //初始化数组以及容量增长
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}

3.接下来看一下Vector的增删

Vector在线程不安全的地方使用了synchronized修饰,因此Vector是线程安全的

public synchronized boolean add(E e) {
    modCount++;//数组修改的次数,该变量在AbstractList定义,不研究
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

在添加元素前,需要调用ensureCapacityHelper()方法,并传递了当前已使用数组长度+1作为最小申请容量

该方法用于判断是否执行扩容,判断条件就是最小申请容量是否大于数组总长度

private void ensureCapacityHelper(int minCapacity) {
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

grow()方法时执行扩容的具体逻辑
1.计算新容量

  • 如果指定了每次扩容增长长度,新容量=旧容量+每次扩容增长长度
  • 如果没有指定,新容量=2 * 旧容量

2.判断新容量大小是否小于最小申请容量(当你需要添加多个元素,而你的每次扩容增长长度又设置的太小)
3.新容量大于最大申请容量时,使用Integer.MAX_VALUE(这里直接看一下,一般不会这么大)

//数组分配的最大容量,没人的数组想要这么大的吧,很占内存的
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
    //1.计算新容量
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    //2.判断新容量大小是否小于最小申请容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //新容量大于最大申请容量时,使用Integer.MAX_VALUE
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) 
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

下面我们来用代码测试一下

public class Test {

    public static void main(String[] args) {
        Vector vector = new Vector(1, 2);
        System.out.println("初始容量:" + vector.capacity());
        vector.add("1");
        System.out.println("添加1个元素后的容量:" + vector.capacity());
        vector.add("2");
        System.out.println("添加2个元素后的容量:" + vector.capacity());
        vector.add("3");
        vector.add("4");
        System.out.println("添加4个元素后的容量:" + vector.capacity());
    }
}
在这里插入图片描述

这里我们了解了Vector是如何进行扩容的,接下来我们看一看Vector如何进行插入

public void add(int index, E element) {
  insertElementAt(element, index);
}

传递了元素和索引,我们来看看insertElementAt()是怎么插入元素的

  1. 判断角标越界没毛病
  2. 判断容量,前面已经讲了,没毛病
  3. 调用System.arraycopy()将数组从索引处整个往后挪
  4. 在索引处设置新的值
public synchronized void insertElementAt(E obj, int index) {
    modCount++; //数组修改的次数,该变量在AbstractList定义,不研究
    if (index > elementCount) {
        throw new ArrayIndexOutOfBoundsException(index
                                                 + " > " + elementCount);
    }
    ensureCapacityHelper(elementCount + 1);
    System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
    elementData[index] = obj;
    elementCount++;
}

下面是System.arraycopy()方法的图解

在这里插入图片描述

下面我们来看看Vector如何进行删除
看到下面是不是绝对和插入非常相似

public synchronized E remove(int index) {
    modCount++;//数组修改的次数,该变量在AbstractList定义,不研究
    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;
}

所以这里也就不再介绍了,至于查询和修改,查询直接返回数组指定索引的值,修改直接修改指定索引的值,没什么说的

你可能感兴趣的:(Vector源码剖析(看不懂直播写检讨))