Vector in Java

Author Avatar
Learn Here Fun Pedia
5 min read •

Vector Deep Dive: Internal Backend Working

The Vector class is a relic of Java's past. Introduced all the way back in JDK 1.0 (before the modern Collections Framework even existed), it is a dynamic, resizable array. On the surface, its architecture is almost identical to an ArrayList.

However, it possesses one foundational difference that dictates how it behaves, why it is slow, and why modern Java developers almost never use it: Every single method in a Vector is synchronized.

[ The Hierarchy ] Iterable ↓ Collection ↓ List ↓ Vector

1. Internal Data Structure

If you look at the raw source code of java.util.Vector, the storage mechanism is straightforward. It relies on three primary fields to manage its data and growth:

public class Vector<E> { protected Object[] elementData; // The actual array protected int elementCount; // Current size (items inserted) protected int capacityIncrement; // Growth rate controller }

Unlike a modern ArrayList which uses lazy allocation (creating the array only when the first element is added), a default Vector immediately allocates an array of 10 slots in memory the moment it is instantiated.


2. The Growth Strategy: 2x Expansion

When an ArrayList fills up, it grows by 1.5x (50%). A Vector takes a much more aggressive approach to avoid frequent resizing operations. By default, when a Vector is completely full, it doubles its capacity (2x).

10
20
30
40
→
Resizes To
→
80

Customizing the Growth Rate

Vector offers a unique constructor that allows you to manually control the exact capacity increment, rather than strictly doubling.

// Initial capacity = 10, Growth increment = 5 Vector<Integer> v = new Vector<>(10, 5);

If you set a custom increment, the Vector will grow linearly ($10 \rightarrow 15 \rightarrow 20 \rightarrow 25$) instead of exponentially. If capacityIncrement is left at 0, it defaults to doubling.


3. The Synchronization Bottleneck

This is the core identity of the Vector class. To make it thread-safe, Java uses an Object Monitor Lock. Look at the method signature for adding an element:

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

Because the method is marked synchronized, only one thread can execute this method at a time across the entire object instance.

  • If Thread A calls v.add(10), it locks the entire Vector.
  • If Thread B wants to call v.get(5), it is completely blocked. It must wait until Thread A finishes inserting its data and releases the lock.

This creates massive Thread Contention. Even in a single-threaded application where no other threads exist, the JVM still wastes CPU cycles acquiring and releasing the monitor lock for every single operation. This is why Vector is fundamentally slower than ArrayList.

The "Check-Then-Act" Race Condition

Many developers mistakenly believe that because a Vector is synchronized, it makes their application completely safe. This is false. Vector only protects individual methods. Compound operations are still highly vulnerable to race conditions.

if (!v.isEmpty()) { // Thread B could remove the last item right here! v.remove(0); // Thread A crashes with ArrayIndexOutOfBoundsException }

To safely execute a check-then-act operation on a Vector, you must still wrap the entire block in an external synchronized(v) block—defeating the entire purpose of using a Vector in the first place.


4. Backend Operations & Complexity

Accessing Data: get()

Because Vector is backed by an array, it utilizes contiguous memory. Fetching data is a simple mathematical address calculation: $\text{Base} + (\text{Index} \times \text{Size})$. This makes random access lightning fast at $O(1)$.

Removing Data: remove()

Removing an item from the middle of a Vector requires shifting all subsequent elements to the left to close the gap. It executes this using System.arraycopy(). Because it requires moving elements, deletion (and middle insertion) operates at $O(n)$.


5. Legacy Enumeration vs. Modern Iterator

Before the Iterator interface was introduced in JDK 1.2, Java used Enumeration to loop over collections. Vector still heavily supports this legacy feature.

Feature Enumeration (Legacy) Iterator (Modern)
Supported By Vector, Hashtable All Collection Classes
Remove Elements? No (Read-only) Yes (Using it.remove())
Fail-Fast? No. Can cause silent corruption if modified during looping. Yes. Throws ConcurrentModificationException instantly.
Method Names hasMoreElements(), nextElement() hasNext(), next()

6. Vector vs. ArrayList: The Ultimate Showdown

Feature Vector ArrayList
Thread Safe? Yes (Method-level locking) No
Performance Slower (Locking overhead) Faster
Growth Strategy Doubles (2x) by default Grows by 1.5x (50%)
Legacy Class? Yes (JDK 1.0) No (Introduced in JDK 1.2)

The Modern Verdict

In modern backend architecture, you should almost never use Vector. It is retained purely for backward compatibility with ancient enterprise systems.

  • If you are working in a single-threaded environment: Use ArrayList.
  • If you need a thread-safe dynamic array: Use Collections.synchronizedList(new ArrayList<>()).
  • If you are building a highly concurrent, read-heavy system: Use CopyOnWriteArrayList, which abandons harsh locking entirely by creating a fresh copy of the array on every write, allowing lock-free reads.

Comments (0)