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.
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:
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).
Customizing the Growth Rate
Vector offers a unique constructor that allows you to manually control the exact capacity increment, rather than strictly doubling.
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:
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.
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
Post a Comment