Java Vector Class
A complete guide to the Java Vector class — thread safety, dynamic resizing, capacity management, Vector vs ArrayList, Enumeration, ListIterator, synchronized collections, and best practices with real-world examples.
Last Updated
March 2026
Read Time
17 min
Level
Beginner to Intermediate
What is the Java Vector Class?
The Vector class in Java is a part of the java.util package and implements a dynamic (resizable) array of objects. Unlike a plain Java array whose size is fixed at creation time, a Vector automatically grows and shrinks as elements are added or removed. It can hold elements of any type (including null) and maintains insertion order.
Vector is one of Java's legacy collection classes, available since Java 1.0 — long before the Collections Framework was introduced in Java 1.2. When the Collections Framework arrived, Vector was retrofitted to implement the List interface, making it compatible with the modern framework. Its most distinctive feature is that all its methods are synchronized, making it inherently thread-safe.
Think of a Vector as a thread-safe, self-expanding shelf. You can add as many books (elements) as you like — the shelf expands automatically when full. And because it is synchronized, two people (threads) can safely add or remove books simultaneously without corrupting the shelf's state.
Vector Syntax and Constructors
The Vector class provides four constructors to control initial capacity and growth behavior:
import java.util.Vector;
// 1. Default constructor — initial capacity 10, doubles when full
Vector<String> v1 = new Vector<>();
// 2. Constructor with initial capacity
Vector<Integer> v2 = new Vector<>(20);
// 3. Constructor with initial capacity AND capacity increment
// grows by 5 each time instead of doubling
Vector<Double> v3 = new Vector<>(10, 5);
// 4. Constructor from existing Collection
java.util.List<String> list = java.util.Arrays.asList("A", "B", "C");
Vector<String> v4 = new Vector<>(list);import java.util.Vector;
public class VectorBasics {
public static void main(String[] args) {
Vector<String> cities = new Vector<>();
// Adding elements
cities.add("Mumbai");
cities.add("Delhi");
cities.add("Bengaluru");
cities.addElement("Chennai"); // legacy method
System.out.println("Cities: " + cities);
System.out.println("Size: " + cities.size());
System.out.println("Capacity: " + cities.capacity());
// Accessing elements
System.out.println("First: " + cities.get(0));
System.out.println("Last: " + cities.lastElement());
// Removing elements
cities.remove("Delhi");
System.out.println("After removal: " + cities);
}
}Output
Cities: [Mumbai, Delhi, Bengaluru, Chennai] Size: 4 Capacity: 10 First: Mumbai Last: Chennai After removal: [Mumbai, Bengaluru, Chennai]Why Use the Vector Class?
While Vector is considered a legacy class, understanding it is essential for Java developers for several reasons:
- ▶
🔒 Built-in Thread Safety — Every method in Vector is synchronized, making it safe to use in multi-threaded environments without any additional synchronization code. This was critical in Java 1.0 before java.util.concurrent existed.
- ▶
📈 Dynamic Resizing — Vector automatically grows when capacity is exhausted. Unlike fixed-size arrays, you never need to pre-know the exact number of elements.
- ▶
🏛️ Legacy Codebase Compatibility — Millions of lines of Java enterprise code use Vector. Reading, maintaining, and modernizing such code requires deep familiarity with Vector's behavior.
- ▶
📋 List Interface Compliance — Vector fully implements the java.util.List interface, so it can be passed anywhere a List is expected — making it interoperable with the modern Collections Framework.
- ▶
📜 Enumeration Support — Vector is the only modern collection that natively supports the legacy Enumeration interface, used in old Java APIs like Properties and Hashtable.
- ▶
🎓 Foundation for Learning — Understanding Vector's design (and its trade-offs) teaches the core principles behind thread-safe collections, capacity management, and why modern alternatives like CopyOnWriteArrayList were designed.
Vector vs ArrayList vs LinkedList
Choosing between Vector, ArrayList, and LinkedList is one of the most common decisions in Java. Here is a definitive comparison:
Rule of thumb: In new single-threaded code, always prefer ArrayList. For thread-safe scenarios, use Collections.synchronizedList(new ArrayList<>()) or CopyOnWriteArrayList. Use Vector only when maintaining or extending legacy Java code.
Vector in the Java Collections Hierarchy
Understanding where Vector sits in Java's Collections hierarchy helps clarify which interfaces it satisfies and which methods it inherits.
Note: Java's Stack class extends Vector, making Vector the parent of Stack. This is why Stack in Java also inherits synchronization. However, both Vector and Stack are considered legacy — prefer ArrayDeque for stack behavior in modern Java.
Important Vector Methods
Vector provides both modern List interface methods and legacy methods from its pre-Collections-Framework days. Knowing both is essential for working with legacy codebases.
Capacity and Increment Management in Vector
One of Vector's most important — and often misunderstood — features is how it manages capacity. Unlike arrays where size is fixed, Vector maintains two separate concepts: size (number of actual elements) and capacity (total allocated storage).
- ▶
📦 Default Capacity — When created with new Vector<>(), the initial capacity is 10. This means Vector pre-allocates space for 10 elements even if you add only 2.
- ▶
📈 Doubling Growth — When no capacityIncrement is set, Vector doubles its capacity each time the size exceeds the capacity. So: 10 → 20 → 40 → 80 → ...
- ▶
🔢 Fixed Increment Growth — If you specify a capacityIncrement (e.g., new Vector<>(10, 5)), the capacity grows by exactly that fixed amount each time: 10 → 15 → 20 → 25 → ...
- ▶
✂️ trimToSize() — Calling trimToSize() reduces the capacity to match the current size, freeing unused memory. Useful after bulk-loading data that will not grow further.
- ▶
🔒 ensureCapacity(n) — Pre-allocates capacity to at least n elements. Useful when you know the approximate final size upfront — avoids multiple resize operations.
import java.util.Vector;
public class CapacityDemo {
public static void main(String[] args) {
// Default: capacity 10, doubles on overflow
Vector<Integer> v = new Vector<>();
System.out.println("Initial capacity: " + v.capacity()); // 10
// Add 11 elements — triggers resize
for (int i = 1; i <= 11; i++) {
v.add(i);
}
System.out.println("Capacity after 11 adds: " + v.capacity()); // 20
System.out.println("Size: " + v.size()); // 11
// trimToSize — frees unused space
v.trimToSize();
System.out.println("Capacity after trimToSize: " + v.capacity()); // 11
// Fixed increment: capacity 5, grows by 3 each time
Vector<String> v2 = new Vector<>(5, 3);
System.out.println("\nv2 initial capacity: " + v2.capacity()); // 5
for (int i = 0; i < 6; i++) v2.add("item" + i);
System.out.println("v2 capacity after 6 adds: " + v2.capacity()); // 8
}
}Output
Initial capacity: 10 Capacity after 11 adds: 20 Size: 11 Capacity after trimToSize: 11 v2 initial capacity: 5 v2 capacity after 6 adds: 8Thread Safety in Vector
Vector achieves thread safety by declaring all its public methods as synchronized. This means the JVM acquires an intrinsic lock (monitor) on the Vector object before executing any method, and releases it afterward. Only one thread can execute any Vector method at a time.
import java.util.Vector;
public class ThreadSafetyDemo {
public static void main(String[] args) throws InterruptedException {
Vector<Integer> sharedVector = new Vector<>();
// Thread 1: adds even numbers
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
sharedVector.add(i * 2);
System.out.println(Thread.currentThread().getName()
+ " added: " + (i * 2));
}
}, "Thread-Even");
// Thread 2: adds odd numbers
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
sharedVector.add(i * 2 + 1);
System.out.println(Thread.currentThread().getName()
+ " added: " + (i * 2 + 1));
}
}, "Thread-Odd");
t1.start();
t2.start();
t1.join();
t2.join();
// No ConcurrentModificationException — Vector is thread-safe
System.out.println("\nFinal Vector (10 elements): " + sharedVector);
System.out.println("Final size: " + sharedVector.size());
}
}Important caveat: Even though individual Vector methods are synchronized, compound operations (check-then-act patterns) are NOT atomically safe. For example, if (!v.isEmpty()) v.remove(0); is NOT thread-safe even with Vector — another thread could remove the last element between the check and the remove. For compound atomic operations, use explicit synchronization or java.util.concurrent classes.
Vector Resize Flow — Flowchart
The flowchart below illustrates exactly how Vector decides whether to resize and by how much when a new element is added.
Code Execution Flow — from source to output
Enumeration and ListIterator in Vector
Vector supports three ways to traverse its elements: the legacy Enumeration, the modern Iterator, and the bidirectional ListIterator. Understanding each is important when working with legacy APIs.
import java.util.*;
public class VectorTraversal {
public static void main(String[] args) {
Vector<String> fruits = new Vector<>();
fruits.add("Mango");
fruits.add("Banana");
fruits.add("Papaya");
fruits.add("Guava");
// --- 1. Legacy Enumeration (Java 1.0) ---
System.out.println("--- Enumeration ---");
Enumeration<String> en = fruits.elements();
while (en.hasMoreElements()) {
System.out.println(en.nextElement());
}
// --- 2. Modern Iterator ---
System.out.println("\n--- Iterator ---");
Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
String fruit = it.next();
if (fruit.equals("Banana")) {
it.remove(); // safe removal during iteration
} else {
System.out.println(fruit);
}
}
// --- 3. ListIterator (bidirectional) ---
System.out.println("\n--- ListIterator (reverse) ---");
ListIterator<String> li = fruits.listIterator(fruits.size());
while (li.hasPrevious()) {
System.out.println(li.previous());
}
}
}Output
--- Enumeration --- Mango Banana Papaya Guava --- Iterator --- Mango Papaya Guava --- ListIterator (reverse) --- Guava Papaya MangoKey difference: Enumeration is read-only and cannot remove elements. Iterator adds safe removal via it.remove(). ListIterator further adds add(), set(), and bidirectional traversal. In modern code, always prefer Iterator or ListIterator over Enumeration.
Best Practices for Using Vector
Knowing when and how to use Vector correctly separates professional Java developers from beginners. Here are the industry-standard best practices:
- ▶
✅ 1. Prefer ArrayList for New Single-Threaded Code — In new non-threaded code, ArrayList always outperforms Vector due to lack of synchronization overhead. Never use Vector by default just because it exists.
- ▶
✅ 2. Use Collections.synchronizedList() for Flexible Thread Safety — For modern synchronized list behavior, Collections.synchronizedList(new ArrayList<>()) gives you thread safety on top of ArrayList without the legacy baggage of Vector.
- ▶
✅ 3. Prefer CopyOnWriteArrayList for Read-Heavy Concurrent Scenarios — When reads vastly outnumber writes, CopyOnWriteArrayList from java.util.concurrent is far more performant than Vector since reads are lock-free.
- ▶
✅ 4. Set Appropriate Initial Capacity — If you know the approximate final size, initialize with new Vector<>(expectedSize) to avoid multiple internal array copies. Avoids performance degradation from repeated resizing.
- ▶
✅ 5. Use trimToSize() After Bulk Loading — After adding a large batch of elements that won't grow further, call trimToSize() to free unused allocated memory.
- ▶
✅ 6. Use Iterator, Not Enumeration, for New Code — Enumeration is a relic from Java 1.0. Use Iterator or enhanced for-loop for traversal. Only use Enumeration when interacting with old APIs that require it (e.g., Properties.keys()).
- ▶
❌ 7. Never Rely on Vector for Atomic Compound Operations — Individual Vector method calls are synchronized, but compound operations (check-then-act) are not. Use explicit synchronized blocks or java.util.concurrent.atomic classes for atomicity.
- ▶
❌ 8. Do Not Mix Legacy and Modern Method Styles — Mixing addElement() with add() and elementAt() with get() in the same codebase creates confusion. Pick one style — modern List methods are preferred for readability and IDE support.
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class VectorAlternatives {
public static void main(String[] args) {
// ✅ Single-threaded — use ArrayList
List<String> singleThreadList = new ArrayList<>();
singleThreadList.add("Item A");
// ✅ Multi-threaded, general — use synchronizedList
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.add("Safe Item");
// ✅ Multi-threaded, read-heavy — use CopyOnWriteArrayList
List<String> cowList = new CopyOnWriteArrayList<>();
cowList.add("Config Value 1");
cowList.add("Config Value 2");
// ✅ Legacy code compatibility — Vector is acceptable here
Vector<String> legacyVector = new Vector<>();
legacyVector.add("Legacy compatible");
System.out.println("ArrayList: " + singleThreadList);
System.out.println("SynchronizedList: " + syncList);
System.out.println("CopyOnWriteArrayList: " + cowList);
System.out.println("Vector: " + legacyVector);
}
}Real-World Code Examples
Example 1 — Task Queue Using Vector (Multi-threaded)
import java.util.Vector;
public class TaskQueue {
private final Vector<String> queue = new Vector<>();
public void enqueue(String task) {
queue.add(task);
System.out.println(Thread.currentThread().getName()
+ " enqueued: " + task + " | Queue size: " + queue.size());
}
public String dequeue() {
if (!queue.isEmpty()) {
String task = queue.remove(0);
System.out.println(Thread.currentThread().getName()
+ " dequeued: " + task);
return task;
}
return null;
}
public static void main(String[] args) throws InterruptedException {
TaskQueue tq = new TaskQueue();
Thread producer = new Thread(() -> {
tq.enqueue("Send Email");
tq.enqueue("Generate Report");
tq.enqueue("Backup Database");
}, "Producer");
Thread consumer = new Thread(() -> {
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
tq.dequeue();
tq.dequeue();
}, "Consumer");
producer.start();
consumer.start();
producer.join();
consumer.join();
System.out.println("Remaining tasks: " + tq.queue);
}
}Output
Producer enqueued: Send Email | Queue size: 1 Producer enqueued: Generate Report | Queue size: 2 Producer enqueued: Backup Database | Queue size: 3 Consumer dequeued: Send Email Consumer dequeued: Generate Report Remaining tasks: [Backup Database]Example 2 — Student Score Manager
import java.util.*;
public class ScoreManager {
static Vector<Integer> scores = new Vector<>(5, 5);
public static void addScore(int score) {
scores.add(score);
}
public static double getAverage() {
int sum = 0;
for (int s : scores) sum += s;
return scores.isEmpty() ? 0 : (double) sum / scores.size();
}
public static int getHighest() {
return Collections.max(scores);
}
public static int getLowest() {
return Collections.min(scores);
}
public static void main(String[] args) {
int[] raw = {88, 74, 95, 61, 83, 79, 92, 55};
for (int s : raw) addScore(s);
System.out.println("Scores: " + scores);
System.out.println("Count: " + scores.size());
System.out.println("Capacity: " + scores.capacity());
System.out.printf ("Average: %.2f%n", getAverage());
System.out.println("Highest: " + getHighest());
System.out.println("Lowest: " + getLowest());
Collections.sort(scores);
System.out.println("Sorted: " + scores);
}
}Output
Scores: [88, 74, 95, 61, 83, 79, 92, 55] Count: 8 Capacity: 10 Average: 78.38 Highest: 95 Lowest: 55 Sorted: [55, 61, 74, 79, 83, 88, 92, 95]Practice This Code — Live Editor
Advantages and Disadvantages of Vector
Like every data structure, Vector has clear strengths and weaknesses. Knowing both helps you make the right choice for the right scenario.
Java Vector — Interview Questions
These are the most frequently asked interview questions on the Java Vector class for Java developer and backend engineer positions.
Practice Questions — Test Your Knowledge
Test your understanding of the Vector class with these practice questions. Attempt each one before revealing the answer.
1. You create Vector<Integer> v = new Vector<>(). You add 15 elements one by one. What is the capacity after adding the 11th element?
Easy2. What is the output? Vector<String> v = new Vector<>(4, 3); for (int i = 0; i < 5; i++) v.add("x"); System.out.println(v.capacity());
Easy3. Why is this compound Vector operation NOT thread-safe, even though Vector is synchronized? if (!v.isEmpty()) { v.remove(0); }
Medium4. You have a Vector with 1000 elements. You call trimToSize(). Then you add one more element. What happens?
Medium5. Can you use a for-each loop on a Vector? What happens if you remove an element inside the loop?
Medium6. You need a thread-safe List for a scenario where there are 100 reads per second but only 2 writes per hour. Which is better: Vector, Collections.synchronizedList, or CopyOnWriteArrayList?
Hard7. What is the output? Vector<Integer> v = new Vector<>(Arrays.asList(5, 3, 8, 1, 9)); Collections.sort(v); System.out.println(v.firstElement() + " " + v.lastElement());
Easy8. Both Vector and Stack are considered legacy classes. What are the modern replacements recommended for their respective use cases?
HardConclusion — Mastering Java Vector
The Vector class is a window into Java's history — it was the original solution for thread-safe, dynamic collections when Java first launched in 1995. Understanding Vector deeply means understanding why modern Java evolved the way it did: why ArrayList was created for performance, why CopyOnWriteArrayList was designed for concurrent reads, and why the Collections Framework standardized a clean interface hierarchy.
In production Java today, Vector rarely appears in new code — but it remains alive in millions of legacy enterprise systems, in the JDK's own Stack class, and in APIs that predate Java 1.2. A complete Java developer is fluent in both the modern idioms and the legacy landscape.
Your next steps: explore CopyOnWriteArrayList and ConcurrentLinkedQueue in java.util.concurrent, study Collections.synchronizedList() internals, and understand how Java 21's Virtual Threads (Project Loom) change the calculus for synchronized collections in high-throughput applications.
Remember: Vector is thread-safe but legacy. ArrayList is fast but single-threaded. CopyOnWriteArrayList is modern, concurrent, and read-optimized. Pick the right tool for your context, and your Java collections code will be correct, performant, and professional. ☕