Android Vector★★

1.Vector

Vector和ArrayList很相似,其内部都是通过一个容量能动态增长的数组来实现的。不同点是Vector是线程安全的。因为其内部有很多同步代码块来保证线程安全。

Vector的大小是可以增加或者减小的,以便适应创建Vector后进行添加或者删除操作。

Vector有几个重要属性:

protected Object[ ] elementData; //底层数组

protected int elementCount; //数组有效元素个数

capacityIncrement  //扩容时的增长系数

2.构造方法

Vector的构造方法一共有四个。

①创建一个空的Vector,并且指定了Vector的初始容量为10。

public Vector(){

    this(10);

}

②创建一个空的Vector,并且指定了Vector的初始容量。

public Vector(int initalCapacity){

    this(initalCapacity,0)

}

③创建一个空的Vector,并且指定了Vector的初始容量和扩容时的增长系数。

public Vector(int initalCapacity, int capacityIncrement){

    super();

    if(initalCapacity < 0)

        throw new IllegalArgumentException("illegal capacity:" + initalCapacity);

    this.elementData = new Object[initalCapacity];

    this.capacityIncrement = capacityIncrement;

}

④根据其他集合来创建一个非空的Vector。

public Vector(Collection<? extends E> c) {

    elementData = c.toArray();

    elementCount = elementData.length;

    if(elementData.getClass() != Object[ ].class)

        elementData = Arrays.copyOf(elementData, elementCount, Object[ ].class);

}

首先把其他集合转化为数组,然后复制粘贴到Vector里面。

3.增加元素

增加元素有两个主要的方法,第一个是在Vector尾部追加,第二个是在指定位置插入元素。

①在Vector尾部追加元素

public synchronized boolean add(E e) {

    modCount++;

    ensureCapacityHelper(elementCount + 1);//判断容量大小,若能装下就直接放进去,装不下就扩容

    elementData[elementCount++] = e;

    return true;

}

然后看一下ensureCapacityHelper是如何实现的:

private void ensureCapacityHelper(int minCapacity) {

    if(minCapacity - elementData.length > 0) 

        grow(minCapacity);

}

所以真正扩容的方法是grow,那再进去看看:

private void grow(int minCapacity) {

    int oldCapacity = elementData.length;

    //capacityIncrement表示需要新增加的数据,如果大于0就扩充指定数量,否则就翻倍

    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);

    //若进行扩容后,capacity仍然小,则新容量改为实例需要的minCapacity大小

    if(newCapacity - minCapacity < 0)

       newCapacity = minCapacity;

    //如果新数组的长度比虚拟机能够提供给数组的最大存储空间还大,则将新数组长度更改为最大正数Integer.MAX_VALUE

    if(newCapacity - MAX_ARRAY_SIZE > 0)

        newCapacity = hugeCapacity(minCapacity);

    //按照新容量newCapacity创建一个新数组,然后再将原数组中的内容复制到新数组中,并将elementData指向新数组

    elementData = Arrays.copyOf(elementData, newCapacity);

}

扩容的时候要排除一些异常的情况,首先就是capacityIncrement(需要增加的数量)是否大于0,如果大于0直接增加这么多。然后发现增加了上面那些还不够,那就扩充为实际需要minCapacity的大小。最后发现还不够,就只能扩充到虚拟机能表示的数字最大值了。

②在指定位置增加元素

public void add(int index, E element){

    insertElementAt(element, index);

}

public synchronized void insertElementAt(E obj, int index){

    modCount++; //fail-fast机制

    //判断index下标的合法性

    if(index > elementCount){

        throw new ArrayIndexOutOfBoundsException (index + ">" + elementCount);

    }

    ensureCapacityHelper(elementCount + 1);

    第四步:数组拷贝,将index到末尾的元素拷贝到index+1到末尾的位置,将index的位置留出来

    System.arrayCopy(elementData, index, elementData, index +1, elementCount - index);

    elementData[index] = obj;

    elementCount++;

}

4.删除元素

删除元素也有两种方法,第一个根据元素值来删除,第二个根据下表来删除元素。

①根据元素值来删除元素

public boolean remove(Object o) {

    return removeElement(o);

}

删除元素其实是调用了removeElement()方法来删除元素的,进入这个方法内部看一下。

public synchronized boolean removeElement( Object obj) {

    modCount++; //fail-fast机制

    int i = indexOf(obj);//查找obj在数组中的下标

    if(i >= 0){ //下标不小于0,说明Vector容器中含有这个元素

        //调用removeElementAt方法删除元素

        removeElementAt(i);

        return true;

    }

    return false;

}

真正执行删除操作的是removeElementAt(i),再进入这个方法看看:

public synchronized void removeElementAt(int index) {

    modCount++; //fail-fast机制

    //index下标合法性检验

    if(index >=elementCount){

        throw new ArrayIndexOutOfBoundsException (index + ">=" + elementCount);

    } else if(index < 0){

        throw new ArrayIndexOutOfBoundsException (index);

    }

    //要移动的元素个数

    int j = elementCount - index - 1;

    if(j > 0){

        //将index之后的元素向前移动一位

        System.arrayCopy(elementData, index + 1, elementData, index, j);

    }

    elementCount--;

    elementData[elementCount] = null;

}

可以看到,删除元素要移动大量的元素,时间效率肯定是不好的。毕竟Vector是通过数组来实现的,而不是通过链表。

②删除指定位置的元素

删除指定位置的元素就比较简单了,到指定的位置进行删除,然后把后面的元素进行移位。

public synchronized E remove(int index) {

    modCount++; //fail-fast机制

    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;

    return oldValue;

}

5.更改元素

public synchronized void setElementAt(E obj, int index) {

    if(index >= elementCount)

        throw new ArrayIndexOutOfBoundsException (index);

    elementData[index] = obj;

}

6.查找元素

查找元素有三个,第一个查询Vector容器中是否包含某个元素,第二个查询第一次出现指定元素的索引,第三个最后一次出现指定元素的索引。

①查询Vector容器中是否包含某个元素

public boolean contains(Object o) {

    return indexOf(o, 0) >= 0;

}

查询Vector是否包含某个元素,其实是调用了第二个方法,那直接就看第二个。

②查询第一次出现的指定元素的索引

public synchronized int indexOf(Object o, int index) {

    if(o == null) {

        for(int i = index; i < elementCount;i++) {

            if(elementData[i] == null)

                return i;

        }

    } else {

        for(int i = index; i < elementCount;i++) {

            if(o.equals(elementData[i]))

                return i;

        }

    }

    return -1;

}

③查询最后一次出现的指定元素的索引

public synchronized int lastIndexOf(Object o, int index){

    if(index >= elementCount)

        throw new ArrayIndexOutOfBoundsException (index + ">=" + elementCount);

    if(o == null) {

        for(int i = index; i >= 0;i--) {

            if(elementData[i] == null)

                return i;

        }

    } else {

        for(int i = index; i >= 0;i--) {

            if(o.equals(elementData[i]))

                return i;

        }

    }

    return -1;

}

7.Vector特点

①线程安全

从Vector的构造方法还有增删改查的操作,可以发现,这些方法都有synchronized关键字,就是这个关键字为Vector容器提供了一个安全机制,保证了线程安全。

②Vector实际上是通过一个数组保存数据的。构造Vecotr时,使用默认构造函数,默认容量大小是10。

③当Vector容量不足以容纳全部元素时,Vector的容量会增加。若容量增加系数大于0,则将容量的值增加“容量增加系数”;否则,将容量大小增加一倍。

8.Vector与其他容器的区别

通过源码可以发现,Vector和ArrayList很相似,下面对比区分一下:

①ArrayList是线程非安全的,因为ArrayList中所有的方法都不是同步的,在并发下一定会出现线程安全问题。而Vector是线程安全的。

②Vector可以指定增长因子,如果该增长因子指定了,那么扩容的时候,每次新的数组大小会在原数组的大小基础上加上增长因子;如果不指定增长因子,那么就扩容到原数组大小的2倍。

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/123962655