Vector implementation principle source code analysis

Vector is a concurrent List class in jdk1.0. Vector and ArrayList implement very similar interfaces and integrate the same classes. Unlike ArrayList, vector is thread safe. synchronized synchronization locks are added to most exposed public methods. Obviously, the performance of vector is not high due to the addition of synchronization locks. To use a thread safe List, CopyOnWriteArrayList is recommended.

1, Class structure of Vector

From the class structure diagram, the class structure system of Vector is basically consistent with ArrayList.

2, Source code analysis

/**
 * An object array that stores elements
 */
protected Object[] elementData;

/**
 * Number of elements
 */
protected int elementCount;

/**
 * Capacity increment: the capacity changes dynamically. If capacityIncrement(=0 or < 0), the capacity will be expanded by twice the capacity
 */
protected int capacityIncrement;

Vector can store any type of elements (including null) and allow repetition. It is basically consistent with ArrayList. Its internal data structure is also an Object array. Capacity expansion is also a dynamic capacity expansion mechanism.

Constructor source code:

/**
 * Create an empty vector object based on the initial capacity and capacity growth factor
 *
 * @param   initialCapacity     Initial capacity
 * @param   capacityIncrement   Capacity growth factor
 * @throws IllegalArgumentException When the initial capacity is negative, an exception will be thrown
 */
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}

/**
 * Overloaded constructor. The default capacity growth factor is 0
 */
public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}

/**
 * Overloaded constructor with initialization capacity of 10 and default capacity growth factor of 0
 */
public Vector() {
    this(10);
}

From the source code, we can see the three basic constructors. When we use new Vector() to create a Vector set, we directly create an Object array with a capacity of 10. Compared with ArrayList, ArrayList is an empty array initialized internally. The capacity is expanded to 10 only when the first element is added, and the capacityIncrement is 0. When the capacity is insufficient, the capacity of the new array will be expanded to twice the capacity of the old array.

Source code for adding elements:

/**
 * Append a new element to the end of the Vector
 *
 * @param e element to be appended to this Vector
 * @return {@code true} (as specified by {@link Collection#add})
 * @since 1.2
 */
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
/**
  * Is a private method that provides public method calls for internal synchronization
  *
  * @see #ensureCapacity(int)
  */
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

This is very simple. When adding an element, the minCapacity passed in is elementCount + 1, which is to add 1 to the actual number of elements of the existing array to determine whether the current array size can accommodate. If not, execute the capacity expansion plan and call the grow(minCapacity) method. The source code is as follows:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//Old capacity
    //If the capacity growth factor is explicitly specified during construction, it shall be increased according to the capacity growth factor. Otherwise, the old capacity shall be doubled, that is, the expansion capacity shall be doubled
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    //The new expansion capacity is enough, that is, use the new expansion capacity, that is, twice the old capacity
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //The new capacity expansion is larger than the maximum value, that is, the maximum value of shaping is used as the capacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //The assignment operation is performed by copying the array
    elementData = Arrays.copyOf(elementData, newCapacity);
}

Compare the grow th method in the ArrayList source code as follows:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);//Growth of 1.5 times
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

From the above code, we can see that the content of the method body of the grow th method is basically the same as that of the ArrayList, and the only difference is the capacity expansion algorithm.

Other operation methods for vector are basically the same as arrayList:

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

    return elementData(index);
}
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 boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
}

The difference is that these methods for array operations add the synchronized keyword and impose synchronization locks on the methods. Of course, Vector is a thread safe dynamic array, but the thread safety implementation means is to add a built-in synchronization lock to all methods of operating the array. When multiple threads operate on Vector, the actual operation mechanism is similar to the serial execution of multiple threads. The efficiency of this method will be very low. After java1.5, The dynamic array object copied during CopyOnWriteArrayList writing is introduced, which will be much more efficient. It is recommended.

Tags: Java

Posted on Wed, 29 Sep 2021 18:53:38 -0400 by Ruud Hermans