Java源码学习--Vector

Java源码学习--Vector

AbstractList的实现类我们常见的就是ArrayList和LinkedList两个,但是Vector也是其一个实现类,我几乎没有使用过这个类,这次就一并来学习一下其源码,看看其和另外两个出名的List相比有何区别。

一、重要属性

Vector的属性和ArrayList相比少了一些:

静态属性

  • serialVersionUID:是一个final修饰的long类型,和ArrayList相同,在序列化和反序列化的时候使用

普通属性

  • elementData:是一个Object[]类型的对象,其作用和ArrayList中的同名对象一样
  • elementCount:是一个int类型的数据,其和ArrayList中的size属性作用一样
  • capacityIncrement:是一个int类型的数据,这个属性是当elementCount的值到达了elementData的容量之后,进行扩容时扩大的容量,当这个值小于等于0时扩容后的容量为当前容量的二倍

二、构造器

Vector的构造器和ArrayList相比,逻辑简单了很多,如果您的记性好的话,应该会记得ArrayList中有两个属性EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA,而他俩的爱恨纠葛在ArrayList的不同的构造器重载以及后续一些方法的调用中都有体现。Vector则简单直白了很多,因为它没有这些花里胡哨的玩意。

Vector的构造器中只有两个是最终被调用的。

第一个

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

可以看到,平平无奇,朴素至极,就是初始化了elementData和capacityIncrement两个属性

public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}  

public Vector() {
    this(10);
}  

可见这两个构造器都是回调的含两个参数的构造器。

第二个

public Vector(Collection 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);
}  

第二个构造器只不过是使用参数中的Collection来初始化属性。

三、重要方法

1. add

作为一个容器,其add方法是最常用的方法:

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}  

这里的ensureCapacityHelper方法是用来检查是否需要扩容的(细节到后面再说),add方法可见就是一个数组的赋值操作,并无任何特点。

add(int index, E element)

该方法将元素添加到指定的位置:

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

public synchronized void insertElementAt(E obj, int index) {
    modCount++;
    if (index > elementCount) {
        throw new ArrayIndexOutOfBoundsException(index
                                                 + " > " + elementCount);
    }
    ensureCapacityHelper(elementCount + 1);
    System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
    elementData[index] = obj;
    elementCount++;
}  

可以看到insertElementAt中首先也要判断是否需要扩容;之后就是将index后面的元素通过System.arraycopy方法实现向后移动一个元素的位置;最后将需要添加的元素插入到对应的位置。该方法由于需要将index后面的元素整体移动一格,所以相对来说还是有一些耗时的。

addAll(Collection c)

有了前面两个方法的铺垫之后,addAll的登场便显得没那么神秘了:

public synchronized boolean addAll(Collection 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;
}  

也是老套路先检查是否需要扩容;之后进行数据的批量添加,同样耗时点在于System.arraycopy

addElement

可能是由于本人太菜了,一直不能理解该方法和add方法同时存在的意义:

public synchronized void addElement(E obj) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = obj;
}  

没错,你没有看错,其只是比add方法少了一个return ture

2. remove

remove方法和add方法是一对儿,也是容器类中必备的方法:

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);
}  

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;
}  

可以看到,remove方法比add方法要麻烦一点,光是找到需要remove掉的对象就需要一个for循环一个一个地找,而之后调用的removeElementAt不用看大家就能猜到干了些什么:

public synchronized void removeElementAt(int index) {
    modCount++;
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }
    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 */
}  

该方法唯二要做的就是把index后面的元素前移动一格;将最后一个元素设置为null

remove(int index)

该方法和removeElementAt方法的实现是一模一样的,只是多了一个return语句:

public synchronized E remove(int index) {
    modCount++;
    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;
}    

removeAllElements

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

    elementCount = 0;
}  

该方法很简单,就是一个for循环将所有的元素赋值为null。

3. set、get方法

set方法和get方法也是一对,分别用来修改和获取数据:

public synchronized E set(int index, E element) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}  

public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    return elementData(index);
}    

4. 其他方法

public synchronized void copyInto(Object[] anArray) {
    System.arraycopy(elementData, 0, anArray, 0, elementCount);
}  

简单粗暴,直接调用System.arraycopy方法将容器内的元素拷贝到参数中。

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;
}  

很简单的功能:size大于元素个数的时候检查是否需要扩容;size小于元素个数的时候将超出size的置为null(注意虽然setSize此时的参数size更小,但是elementData属性的length是不会变小的,只是elementCount变小了,也即只存在扩容操作,不存在缩容的情况)

public synchronized int capacity() {
    return elementData.length;
}  

这个方法印象中ArrayList是没有提供的,该方法返回的是elementData的长度,其大于等于elementCount(size()方法的返回值)

public boolean contains(Object o) {
    return indexOf(o, 0) >= 0;
}  

public int indexOf(Object o) {
    return indexOf(o, 0);
}  

public synchronized int lastIndexOf(Object o) {
    return lastIndexOf(o, elementCount-1);
}  

这三个方法也是形影不离的,很简单,都是for循环搞定。

5. 令人费解的方法

唉,又是一堆方法,个人感觉有些已有的方法和这些都已经重复了,但是不知道为何还要保留出来:

  public synchronized E elementAt(int index) {
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
    }

    return elementData(index);
}   

该方法和get方法完全一样

public synchronized E firstElement() {
    if (elementCount == 0) {
        throw new NoSuchElementException();
    }
    return elementData(0);
}    

public synchronized E lastElement() {
    if (elementCount == 0) {
        throw new NoSuchElementException();
    }
    return elementData(elementCount - 1);
}  

这两个方法分别等价于get(0)与get(size())

public synchronized void setElementAt(E obj, int index) {
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    elementData[index] = obj;
}  

该方法和set方法几乎一模一样,只是set方法需要返回修改前的数据

四、迭代器

Vector和ArrayList很想,其也有两个方法来获取迭代器:

public synchronized ListIterator listIterator() {
    return new ListItr(0);
}  

public synchronized Iterator iterator() {
    return new Itr();
}  

当然类比ArrayList不用想也知道Itr是ListItr的直接父类,子类相较于父类,添加了很多方法(内部维护了modCount一致)来提供更多的功能

1. Itr

Vector的Itr的实现和ArrayList的差不多,三个属性都是一模一样的,直接copy来:

  • cursor:是一个int值,代表当前所在的位置,其范围是[0, elementCount]
  • lastRet:是一个int值,默认为-1,代表最后一次调用next方法返回的数据的位置
  • expectedModCount:为一个int值,初始化时赋值为了Vector的modCount

重要方法

public E next() {
        synchronized (Vector.this) {
            checkForComodification();
            int i = cursor;
            if (i >= elementCount)
                throw new NoSuchElementException();
            cursor = i + 1;
            return elementData(lastRet = i);
        }
    }

    public void remove() {
        if (lastRet == -1)
            throw new IllegalStateException();
        synchronized (Vector.this) {
            checkForComodification();
            Vector.this.remove(lastRet);
            expectedModCount = modCount;
        }
        cursor = lastRet;
        lastRet = -1;
    }  

如果你不限麻烦的话,可以发现和ArrayList相比,这几个方法的内容几乎是一模一样的,只是使用了synchronize修饰了一番。

2. ListItr

final class ListItr extends Itr implements ListIterator {
    ListItr(int index) {
        super();
        cursor = index;
    }

    public boolean hasPrevious() {
        return cursor != 0;
    }

    public int nextIndex() {
        return cursor;
    }

    public int previousIndex() {
        return cursor - 1;
    }

    public E previous() {
        synchronized (Vector.this) {
            checkForComodification();
            int i = cursor - 1;
            if (i < 0)
                throw new NoSuchElementException();
            cursor = i;
            return elementData(lastRet = i);
        }
    }

    public void set(E e) {
        if (lastRet == -1)
            throw new IllegalStateException();
        synchronized (Vector.this) {
            checkForComodification();
            Vector.this.set(lastRet, e);
        }
    }

    public void add(E e) {
        int i = cursor;
        synchronized (Vector.this) {
            checkForComodification();
            Vector.this.add(i, e);
            expectedModCount = modCount;
        }
        cursor = i + 1;
        lastRet = -1;
    }
}  

唉,偷个懒,可以发现相较于其直接父类Itr,增加了previous、add、set三个方法;其细节和注意点和Itr是一以贯之的。

五、总结

可以看到Vector和ArrayList几乎是一个模子里刻出来的,Vector唯一能拿的出手的好像只有一点:其是线程安全的,这得益于其所有方法(或者是内部的代码块)都是用了synchronize修饰了的,但是这种简单粗暴的实现也宣告着Vector在高并发情况下的表现也是不尽如人意的。

你可能感兴趣的:(Java源码学习--Vector)