Detailed introduction (source code analysis) and use examples of Vector of Java Collection Series 06

Posted by pitstop on Fri, 14 Jan 2022 04:17:57 +0100

outline

After learning arrayList and LinkedList, we continue to learn Vector

Part 1 Introduction to Vector

Introduction to Vector

Vector is a vector queue, which is jdk1 Class added by version 0. It inherits from AbstractList and implements list, randomaccess and clonable interfaces.
Vector inherits AbstractList and implements List; Therefore, it is a queue that supports related functions such as addition, deletion, modification and traversal.
Vector implements RandmoAccess interface, which provides random access function. RandmoAccess is implemented by List in java to provide fast access for List. In vector, we can quickly obtain the element object through the element serial number; This is fast random access.
Vector implements the clonable interface, that is, the clone() function. It can be cloned.

Unlike ArrayList, operations in Vector are thread safe.

Constructor for Vector

Vector There are four constructors
// Default constructor 
Vector()

// Capacity is the default capacity size of the Vector. When the capacity increases due to increasing data, the capacity will double each time.
Vector(int capacity)

// Capacity is the default capacity of the Vector, and capacityincrease is the increment value each time the Vector capacity increases.
Vector(int capacity, int capacityIncrement)

// Create a Vector containing collection
Vector(Collection<? extends E> collection)

  

Vector API

synchronized boolean        add(E object)
             void           add(int location, E object)
synchronized boolean        addAll(Collection<? extends E> collection)
synchronized boolean        addAll(int location, Collection<? extends E> collection)
synchronized void           addElement(E object)
synchronized int            capacity()
             void           clear()
synchronized Object         clone()
             boolean        contains(Object object)
synchronized boolean        containsAll(Collection<?> collection)
synchronized void           copyInto(Object[] elements)
synchronized E              elementAt(int location)
             Enumeration<E> elements()
synchronized void           ensureCapacity(int minimumCapacity)
synchronized boolean        equals(Object object)
synchronized E              firstElement()
             E              get(int location)
synchronized int            hashCode()
synchronized int            indexOf(Object object, int location)
             int            indexOf(Object object)
synchronized void           insertElementAt(E object, int location)
synchronized boolean        isEmpty()
synchronized E              lastElement()
synchronized int            lastIndexOf(Object object, int location)
synchronized int            lastIndexOf(Object object)
synchronized E              remove(int location)
             boolean        remove(Object object)
synchronized boolean        removeAll(Collection<?> collection)
synchronized void           removeAllElements()
synchronized boolean        removeElement(Object object)
synchronized void           removeElementAt(int location)
synchronized boolean        retainAll(Collection<?> collection)
synchronized E              set(int location, E object)
synchronized void           setElementAt(E object, int location)
synchronized void           setSize(int length)
synchronized int            size()
synchronized List<E>        subList(int start, int end)
synchronized <T> T[]        toArray(T[] contents)
synchronized Object[]       toArray()
synchronized String         toString()
synchronized void           trimToSize()
 

Part 2 Vector data structure

Inheritance of Vector

java.lang.Object
   ↳     java.util.AbstractCollection<E>
         ↳     java.util.AbstractList<E>
               ↳     java.util.Vector<E>

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

The relationship between Vector and Collection is shown in the following figure:

The data structure of Vector is similar to that of ArrayList. It contains three member variables: elementData, elementCount and capacityIncrement.

(01) elementData is an "array of Object [] type", which saves the elements added to the Vector. elementData is a dynamic array. If the > size of the dynamic array is not specified when initializing the Vector, the default size of 10 is used. With the increase of elements in the Vector, the capacity of the Vector will also grow dynamically. capacityIncrement is the growth coefficient related to the capacity growth. For the specific growth mode, please refer to the ensureCapacity() function in the source code analysis.

(02) elementCount is the actual size of the dynamic array.

(03) capacityIncrement is the growth factor of dynamic array. If the size of capacityIncrement is specified when creating a Vector; Then, every time the dynamic array capacity in the Vector increases, the increased size is capacityIncrement.

Part 3 Vector source code analysis (based on JDK1.6.0_45)

In order to better understand the principle of Vector, the source code of Vector is analyzed below.  

package java.util;

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

    // An array that holds the data in the Vector
    protected Object[] elementData;

    // Quantity of actual data
    protected int elementCount;

    // Capacity growth factor
    protected int capacityIncrement;

    // Serial version number of Vector
    private static final long serialVersionUID = -2767605614048989439L;

    // Vector constructor. The default capacity is 10.
    public Vector() {
        this(10);
    }

    // Constructor that specifies the size of the Vector capacity
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

    // Specifies the constructor for Vector capacity size and growth factor
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        // Create a new array with the capacity of initialCapacity
        this.elementData = new Object[initialCapacity];
        // Set capacity growth factor
        this.capacityIncrement = capacityIncrement;
    }

    // Specifies the Vector constructor for the collection.
    public Vector(Collection<? extends E> c) {
        // Get the array of "set (c)" and assign it to elementData
        elementData = c.toArray();
        // Set array length
        elementCount = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
    }

    // Copy all the elements of the array Vector into the array anArray
    public synchronized void copyInto(Object[] anArray) {
        System.arraycopy(elementData, 0, anArray, 0, elementCount);
    }

    // Set the current capacity value to = actual number of elements
    public synchronized void trimToSize() {
        modCount++;
        int oldCapacity = elementData.length;
        if (elementCount < oldCapacity) {
            elementData = Arrays.copyOf(elementData, elementCount);
        }
    }

    // Confirm the help function for Vector capacity
    private void ensureCapacityHelper(int minCapacity) {
        int oldCapacity = elementData.length;
        // When the capacity of the Vector is insufficient to accommodate all the current elements, increase the capacity.
        // If the capacity increment factor is > 0 (i.e. capacityIncrement > 0), the capacity will be increased
        // Otherwise, double the capacity.
        if (minCapacity > oldCapacity) {
            Object[] oldData = elementData;
            int newCapacity = (capacityIncrement > 0) ?
                (oldCapacity + capacityIncrement) : (oldCapacity * 2);
            if (newCapacity < minCapacity) {
                newCapacity = minCapacity;
            }
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }

    // Determine the capacity of the Vector.
    public synchronized void ensureCapacity(int minCapacity) {
        // Add the change statistics of Vector + 1
        modCount++;
        ensureCapacityHelper(minCapacity);
    }

    // Set the capacity value to newSize
    public synchronized void setSize(int newSize) {
        modCount++;
        if (newSize > elementCount) {
            // If "newSize is greater than Vector capacity", adjust the size of the Vector.
            ensureCapacityHelper(newSize);
        } else {
            // If "newSize is less than / equal to Vector capacity", set the elements starting at the newSize position to null
            for (int i = newSize ; i < elementCount ; i++) {
                elementData[i] = null;
            }
        }
        elementCount = newSize;
    }

    // Returns the total capacity of the Vector
    public synchronized int capacity() {
        return elementData.length;
    }

    // Returns "the actual size of the Vector", that is, the number of elements in the Vector
    public synchronized int size() {
        return elementCount;
    }

    // Judge whether the Vector is empty
    public synchronized boolean isEmpty() {
        return elementCount == 0;
    }

    // Return "Enumeration corresponding to all elements in Vector"
    public Enumeration<E> elements() {
        // Enumeration is implemented through anonymous classes
        return new Enumeration<E>() {
            int count = 0;

            // Does the next element exist
            public boolean hasMoreElements() {
                return count < elementCount;
            }

            // Get next element
            public E nextElement() {
                synchronized (Vector.this) {
                    if (count < elementCount) {
                        return (E)elementData[count++];
                    }
                }
                throw new NoSuchElementException("Vector Enumeration");
            }
        };
    }

    // Returns whether the Vector contains objects (o)
    public boolean contains(Object o) {
        return indexOf(o, 0) >= 0;
    }


    // Find the element (o) backwards from the index position.
    // If found, the index value of the element is returned; Otherwise, - 1 is returned
    public synchronized int indexOf(Object o, int index) {
        if (o == null) {
            // If the lookup element is null, find the null element forward and return its corresponding sequence number
            for (int i = index ; i < elementCount ; i++)
            if (elementData[i]==null)
                return i;
        } else {
            // If the lookup element is not null, find the element forward and return its corresponding sequence number
            for (int i = index ; i < elementCount ; i++)
            if (o.equals(elementData[i]))
                return i;
        }
        return -1;
    }

    // Find and return the index value of element (o) in Vector
    public int indexOf(Object o) {
        return indexOf(o, 0);
    }

    // Find element (o) from back to front. And returns the index of the element
    public synchronized int lastIndexOf(Object o) {
        return lastIndexOf(o, elementCount-1);
    }

    // Find element (o) from back to front. The starting position is the number from front to back;
    // If found, the "index value" of the element is returned; Otherwise, - 1 is returned.
    public synchronized int lastIndexOf(Object o, int index) {
        if (index >= elementCount)
            throw new IndexOutOfBoundsException(index + " >= "+ elementCount);

        if (o == null) {
            // If the lookup element is null, find the null element in reverse and return its corresponding sequence number
            for (int i = index; i >= 0; i--)
            if (elementData[i]==null)
                return i;
        } else {
            // If the lookup element is not null, find the element in reverse and return its corresponding sequence number
            for (int i = index; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
        }
        return -1;
    }

    // Returns the element at the index position in the Vector.
    // If the index month is closed, an exception is thrown
    public synchronized E elementAt(int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
        }

        return (E)elementData[index];
    }

    // Gets the first element in the Vector.
    // If it fails, an exception will be thrown!
    public synchronized E firstElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return (E)elementData[0];
    }

    // Gets the last element in the Vector.
    // If it fails, an exception will be thrown!
    public synchronized E lastElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return (E)elementData[elementCount - 1];
    }

    // Set the element value of the index position to obj
    public synchronized void setElementAt(E obj, int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                 elementCount);
        }
        elementData[index] = obj;
    }

    // Delete element at index position
    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 */
    }

    // Insert element (obj) at index position
    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++;
    }

    // Add element obj to the end of the Vector
    public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }

    // Find and delete the element obj in the Vector.
    // If successful, return true; Otherwise, false is returned.
    public synchronized boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
    }

    // Delete all elements in Vector
    public synchronized void removeAllElements() {
        modCount++;
        // Set all elements in the Vector to null
        for (int i = 0; i < elementCount; i++)
            elementData[i] = null;

        elementCount = 0;
    }

    // Clone function
    public synchronized Object clone() {
        try {
            Vector<E> v = (Vector<E>) super.clone();
            // Copy all the elements of the current Vector into v
            v.elementData = Arrays.copyOf(elementData, elementCount);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError();
        }
    }

    // Return Object array
    public synchronized Object[] toArray() {
        return Arrays.copyOf(elementData, elementCount);
    }

    // Returns the template array of Vector. The so-called template array, that is, T can be set to any data type
    public synchronized <T> T[] toArray(T[] a) {
        // If the size of array a < the number of elements of vector;
        // Create a new T [] array with the size of "number of elements of Vector" and copy all "Vector" into the new array
        if (a.length < elementCount)
            return (T[]) Arrays.copyOf(elementData, elementCount, a.getClass());

        // If the size of array a > = the number of elements of vector;
        // All elements of the Vector are copied to array a.
    System.arraycopy(elementData, 0, a, 0, elementCount);

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

        return a;
    }

    // Gets the element at the index location
    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return (E)elementData[index];
    }

    // Set the value of the index position to element. And returns the original value of the index position
    public synchronized E set(int index, E element) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

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

    // Add element e to the end of the Vector.
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

    // Delete the element in the Vector o
    public boolean remove(Object o) {
        return removeElement(o);
    }

    // Add the element element at the index position
    public void add(int index, E element) {
        insertElementAt(element, index);
    }

    // Delete the element at the index position and return the original value of the index position
    public synchronized E remove(int index) {
        modCount++;
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        Object 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 (E)oldValue;
    }

    // Empty Vector
    public void clear() {
        removeAllElements();
    }

    // Returns whether the Vector contains the set c
    public synchronized boolean containsAll(Collection<?> c) {
        return super.containsAll(c);
    }

    // Add set c to Vector
    public synchronized boolean addAll(Collection<? extends E> c) {
        modCount++;
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityHelper(elementCount + numNew);
        // Copy all the elements of set c to the array elementData
        System.arraycopy(a, 0, elementData, elementCount, numNew);
        elementCount += numNew;
        return numNew != 0;
    }

    // Delete all elements of set c
    public synchronized boolean removeAll(Collection<?> c) {
        return super.removeAll(c);
    }

    // Delete "elements not in set c"
    public synchronized boolean retainAll(Collection<?> c)  {
        return super.retainAll(c);
    }

    // Starting at the index position, add the set c to the Vector
    public synchronized boolean addAll(int index, Collection<? extends E> c) {
        modCount++;
        if (index < 0 || index > elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityHelper(elementCount + numNew);

        int numMoved = elementCount - index;
        if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew, numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        elementCount += numNew;
        return numNew != 0;
    }

    // Returns whether two objects are equal
    public synchronized boolean equals(Object o) {
        return super.equals(o);
    }

    // DP Hash 
    public synchronized int hashCode() {
        return super.hashCode();
    }

    // Call the toString() of the parent class
    public synchronized String toString() {
        return super.toString();
    }

    // Gets the subset from fromindex (included) to toindex (excluded) in the Vector
    public synchronized List<E> subList(int fromIndex, int toIndex) {
        return Collections.synchronizedList(super.subList(fromIndex, toIndex), this);
    }

    // Delete the elements from fromIndex to toIndex in the Vector
    protected synchronized void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = elementCount - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // Let gc do its work
        int newElementCount = elementCount - (toIndex-fromIndex);
        while (elementCount != newElementCount)
            elementData[--elementCount] = null;
    }

    // java.io.Serializable write function
    private synchronized void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        s.defaultWriteObject();
    }
}

Summary:
(01) Vector actually saves data through an array. When we construct Vecotr; If the default constructor is used, the default capacity size of the Vector is 10.
(02) when the capacity of the Vector is insufficient to accommodate all elements, the capacity of the Vector will increase. If the capacity increase factor is > 0, increase the capacity value by "capacity increase factor"; Otherwise, double the capacity.
(03) the cloning function of vector is to clone all elements into an array.

 

Part 4 Vector traversal mode

Vector supports four traversal modes. It is recommended to use the following second method to traverse the vector because of efficiency problems.

(01) the first one is traversed by iterators. That is, traverse through the Iterator.

Integer value = null;
int size = vec.size();
for (int i=0; i<size; i++) {
    value = (Integer)vec.get(i);        
}

(02) the second, random access, traverses through the index value.
Since Vector implements the RandomAccess interface, it supports random access to elements through index values.

Integer value = null;
int size = vec.size();
for (int i=0; i<size; i++) {
    value = (Integer)vec.get(i);        
}

(03) the third, the other for loop. As follows:

Integer value = null;
for (Integer integ:vec) {
    value = integ;
}

(04) the fourth method is Enumeration traversal. As follows:

Integer value = null;
Enumeration enu = vec.elements();
while (enu.hasMoreElements()) {
    value = (Integer)enu.nextElement();
}

The code to test the efficiency of these traversal methods is as follows:

 

import java.util.*;

/*
 * @desc Vector Traversal mode and efficiency test program.
 *
 * @author skywang
 */
public class VectorRandomAccessTest {

    public static void main(String[] args) {
        Vector vec= new Vector();
        for (int i=0; i<100000; i++)
            vec.add(i);
        iteratorThroughRandomAccess(vec) ;
        iteratorThroughIterator(vec) ;
        iteratorThroughFor2(vec) ;
        iteratorThroughEnumeration(vec) ;

    }

    private static void isRandomAccessSupported(List list) {
        if (list instanceof RandomAccess) {
            System.out.println("RandomAccess implemented!");
        } else {
            System.out.println("RandomAccess not implemented!");
        }

    }

    public static void iteratorThroughRandomAccess(List list) {

        long startTime;
        long endTime;
        startTime = System.currentTimeMillis();
        for (int i=0; i<list.size(); i++) {
            list.get(i);
        }
        endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println("iteratorThroughRandomAccess: " + interval+" ms");
    }

    public static void iteratorThroughIterator(List list) {

        long startTime;
        long endTime;
        startTime = System.currentTimeMillis();
        for(Iterator iter = list.iterator(); iter.hasNext(); ) {
            iter.next();
        }
        endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println("iteratorThroughIterator: " + interval+" ms");
    }


    public static void iteratorThroughFor2(List list) {

        long startTime;
        long endTime;
        startTime = System.currentTimeMillis();
        for(Object obj:list)
            ;
        endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println("iteratorThroughFor2: " + interval+" ms");
    }

    public static void iteratorThroughEnumeration(Vector vec) {

        long startTime;
        long endTime;
        startTime = System.currentTimeMillis();
        for(Enumeration enu = vec.elements(); enu.hasMoreElements(); ) {
            enu.nextElement();
        }
        endTime = System.currentTimeMillis();
        long interval = endTime - startTime;
        System.out.println("iteratorThroughEnumeration: " + interval+" ms");
    }
}

Operation results:

iteratorThroughRandomAccess: 6 ms
iteratorThroughIterator: 9 ms
iteratorThroughFor2: 8 ms
iteratorThroughEnumeration: 7 ms

Summary: traversing Vector, random access using index is the fastest and iterator is the slowest.

 

Part 5 Vector example

Let's learn how to use Vector by example

import java.util.Vector;
import java.util.List;
import java.util.Iterator;
import java.util.Enumeration;

/**
 * @desc Vector Test functions: traversing Vector and common API s
 *
 * @author skywang
 */
public class VectorTest {
    public static void main(String[] args) {
        // New Vector
        Vector vec = new Vector();

        // Add element
        vec.add("1");
        vec.add("2");
        vec.add("3");
        vec.add("4");
        vec.add("5");

        // Set the first element to 100
        vec.set(0, "100");
        // Insert "500" into position 3
        vec.add(2, "300");
        System.out.println("vec:"+vec);

        // (sequential lookup) get an index of 100
        System.out.println("vec.indexOf(100):"+vec.indexOf("100"));
        // (find in reverse order) get the index of 100
        System.out.println("vec.lastIndexOf(100):"+vec.lastIndexOf("100"));
        // Get the first element
        System.out.println("vec.firstElement():"+vec.firstElement());
        // Get the 3rd element
        System.out.println("vec.elementAt(2):"+vec.elementAt(2));
        // Get the last element
        System.out.println("vec.lastElement():"+vec.lastElement());

        // Gets the size of the Vector
        System.out.println("size:"+vec.size());
        // Gets the total capacity of the Vector
        System.out.println("capacity:"+vec.capacity());

        // Gets the "2nd" to "4th" elements of the vector
        System.out.println("vec 2 to 4:"+vec.subList(1, 4));

        // Traverse Vector through Enumeration
        Enumeration enu = vec.elements();
        while(enu.hasMoreElements())
            System.out.println("nextElement():"+enu.nextElement());

        Vector retainVec = new Vector();
        retainVec.add("100");
        retainVec.add("300");
        // Gets the collection of elements contained in 'retainVec' in 'vec'
        System.out.println("vec.retain():"+vec.retainAll(retainVec));
        System.out.println("vec:"+vec);

        // Get String array corresponding to vec
        String[] arr = (String[]) vec.toArray(new String[0]);
        for (String str:arr)
            System.out.println("str:"+str);

        // Empty Vector. clear() is the same as removeAllElements()!
        vec.clear();
//        vec.removeAllElements();

        // Judge whether the Vector is empty
        System.out.println("vec.isEmpty():"+vec.isEmpty());
    }
}

Operation results:

vec:[100, 2, 300, 3, 4, 5]
vec.indexOf(100):0
vec.lastIndexOf(100):0
vec.firstElement():100
vec.elementAt(2):300
vec.lastElement():5
size:6
capacity:10
vec 2 to 4:[2, 300, 3]
nextElement():100
nextElement():2
nextElement():300
nextElement():3
nextElement():4
nextElement():5
vec.retain():true
vec:[100, 300]
str:100
str:300
vec.isEmpty():true
 

 

Topics: vector