Java源码分析Vector源码分析

Posted 流动的城市

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java源码分析Vector源码分析相关的知识,希望对你有一定的参考价值。

类的声明

public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

仔细对比ArrayList的类的定义就可以看出,Vector实现的接口以及父类和ArrayList是一样的。实际上从继承体系上也可以看出两者很类似,同属于List的实现类。

  1. Vector是一个实现了自增长的数组的对象集合,由于底层实现是数组,所以支持下标访问。同ArrayList一样,Vector的大小也是支持增长grow和缩小shrink的
  2. Vector对象为了优化存储结构通常会维护一个capacity变量和一个capacityIncrement变量。capacity是和容量大小略大的值,但是Vector增长的时候并不是按ArrayList那种增加为原来的1.5倍,而是增加capacityIncrement大小。这种方式可以在知道需要多少存储空间来存储将要添加的对象的时候,一次性申请足够的存储空间,避免频繁的增加Vector的大小
  3. 指向Vector的迭代器同样会因为遍历的时候修改Vector而导致ConcurrentModification异常而出现fail-fast现象
  4. Vector并不是一开始就存在于List,而是后来1.2之后添加的。Vector和其他List的最大的不同点在于其线程安全性,正是因为实现了线程安全性,导致在单线程下性能比不上ArrayList,所以如果并不在多线程环境下使用,还是建议使用ArrayList

    protected Object[] elementData; // 存储数据的数组
    protected int elementCount; // 集合中的对象个数
    protected int capacityIncrement; // 增长尺度

构造函数

// 1
public Vector() 
    this(10);


// 2
public Vector(int initialCapacity) 
    this(initialCapacity, 0);


// 3 
public Vector(int initialCapacity, int capacityIncrement) 
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
   

// 4
public Vector(Collection<? extends E> c) 
    elementData = c.toArray();
    elementCount = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);

四个构造函数,很明显无参构造的时候默认是构造一个初始容量为10,增长尺度为0的空集合,构造函数2和3一样根据指定的初始容量和增长尺度构造空集合,默认增长尺度都是0.第四个构造函数根据给定集合中的数据初始化一个新的Vector

存储空间收缩

public synchronized void trimToSize() 
    modCount++;
    int oldCapacity = elementData.length;
    if (elementCount < oldCapacity) 
        elementData = Arrays.copyOf(elementData, elementCount);
    

将实际的容量capacity调整为实际存储的数据个数。这里之所以需要拷贝一下,是为了将就的大存储空间释放,开辟新的较小的存储空间来存储剩余对象。此外,可以看到由于线程安全性,该方法使用了同步关键字

扩容

public synchronized void ensureCapacity(int minCapacity) 
    if (minCapacity > 0) 
        modCount++;
        ensureCapacityHelper(minCapacity);
    


private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

// ensureCapacityHelper()最终调用了grow()方法
private void grow(int minCapacity) 
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);

新扩展的大小为旧容量加上capacityIncrement。如果capacityIncrement小于等于0,那么新的容量将会是原来旧容量的两倍这一点可以从代码上看出来。注意grow()方法没有做线程同步,但是它实际上是被内部的线程同步函数调用的,所以不会发生不一致的问题。

设置大小

public synchronized void setSize(int newSize) 
    modCount++;
    if (newSize > elementCount) 
        ensureCapacityHelper(newSize);
     else 
        for (int i = newSize ; i < elementCount ; i++) 
            elementData[i] = null;
        
    
    elementCount = newSize;

在设置大小的过程中存在两种情况,一种是新的大小比原来的大,那么多于的空位置会置为NULL;另一种情况是新的大小比原来的小,那么下标比newSize大的元素都被丢弃

将Vector中对象转换为枚举

public Enumeration<E> elements() 
    return new Enumeration<E>() 
        int count = 0;

        public boolean hasMoreElements() 
            return count < elementCount;
        

        public E nextElement() 
            synchronized (Vector.this) 
                if (count < elementCount) 
                    return elementData(count++);
                
            
            throw new NoSuchElementException("Vector Enumeration");
        
    ;

增删方法,这些实现和ArrayList类似,只是增加了Synchrnized关键字进行多线程的同步,例如删除所有元素

public synchronized void removeAllElements() 
    modCount++;
    // Let gc do its work
    for (int i = 0; i < elementCount; i++)
        elementData[i] = null;

    elementCount = 0;

该方法将Vector中的对象全部删除,并且将对象个数置为0

添加

public synchronized boolean addAll(Collection<? extends E> c) 
    modCount++;
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityHelper(elementCount + numNew);
    System.arraycopy(a, 0, elementData, elementCount, numNew);
    elementCount += numNew;
    return numNew != 0;

将c中的所有数据添加到Vector 的尾部。存在一种异常情况就是参数c就是this,此时程序的行为使不确定的

删除和保留指定集合中的元素

public synchronized boolean retainAll(Collection<?> c) 
    return super.retainAll(c);


public synchronized boolean removeAll(Collection<?> c) 
    return super.removeAll(c);

前者保留同时位于Vector中和c中的数据,其他数据删除;后者是删除既在Vector中又在c中的数据

查找指定对象的下标

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;

该方法的第二个参数index说明实际的查找起点是从index开始,默认为0。从该函数也可以看出Vector中也是允许NULL的存在的

转换为Array

public synchronized Object[] toArray() 
    return Arrays.copyOf(elementData, elementCount);


@SuppressWarnings("unchecked")
public synchronized <T> T[] toArray(T[] a) 
    if (a.length < elementCount)
        return (T[]) Arrays.copyOf(elementData, elementCount, a.getClass());

    System.arraycopy(elementData, 0, a, 0, elementCount);

    if (a.length > elementCount)
        a[elementCount] = null;

    return a;

第二个泛型toAyyay和之前的也是一样的,在a.length < elementCount也就是存储空间不足的时候,会新建一个新的存储空间然后拷贝到Array中,否则,直接拷贝到a中,a中剩余部分直接置为NULL

相等性判断

public synchronized boolean equals(Object o) 
    return super.equals(o);

这个方法之前是没有的,相等的条件是比较严格的,还有当o也是一个List并且他们的size 想的,元素相等,并且元素的顺序是一样的。也就是对应位置的元素是相等的,此时才返回true

返回一个子List

public synchronized List<E> subList(int fromIndex, int toIndex) 
    return Collections.synchronizedList(super.subList(fromIndex, toIndex),
                                        this);

返回的子List支持所有List的操作。主要用于区间操作的时候返回一个子区间的数据,类似于数据库中的表和视图的关系,正式因为如此,对于所返回的subList的操作最终会反映到Vector中,相应的对Vector的数据操作也会反映到subList中。例如list.subList(from, to).clear();操作改变的是实际的Vector.但是如果对Vector的操作是增删等改变结构的操作,那么所返回的这个subList就失效了

序列化

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException 
    final java.io.ObjectOutputStream.PutField fields = s.putFields();
    final Object[] data;
    synchronized (this) 
        fields.put("capacityIncrement", capacityIncrement);
        fields.put("elementCount", elementCount);
        data = elementData.clone();
    
    fields.put("elementData", data);
    s.writeFields();

将Vector中的数据序列化到流中,注意该方法是私有方法

Java中实际使用Vector远远没有C++中使用的普遍,可能是平时写多线程的代码不多,或者Vector本身替代性太强了。

以上是关于Java源码分析Vector源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Java集合源码分析之Vector

Vector源码分析

java集合13——— Stack源码分析走一波

7-java安全基础——ArrayList,Vector源码分析

7-java安全基础——ArrayList,Vector源码分析

7-java安全基础——ArrayList,Vector源码分析