Vector
Vector is Java’s original resizable array — it predates the Collections Framework and has been part of Java since version 1.0. Today it is considered a legacy class, but it is still used in codebases that need a thread-safe list without reaching for external concurrency utilities.
What is Vector?
Vector<E> lives in java.util and implements the List interface, so it behaves like any other list. The defining trait that separates it from ArrayList is that every public method is synchronized, meaning only one thread can read or modify the Vector at a time.
import java.util.Vector;
public class VectorBasics {
public static void main(String[] args) {
Vector<String> cities = new Vector<>();
cities.add("Delhi");
cities.add("Mumbai");
cities.add("Bangalore");
System.out.println(cities);
System.out.println("Size: " + cities.size());
}
}
Output:
[Delhi, Mumbai, Bangalore]
Size: 3
Creating a Vector
Vector provides four constructors, giving you control over initial capacity and the increment step.
import java.util.Vector;
public class VectorConstructors {
public static void main(String[] args) {
// Default capacity = 10
Vector<Integer> v1 = new Vector<>();
// Initial capacity of 20
Vector<Integer> v2 = new Vector<>(20);
// Initial capacity 20, grows by 5 each time capacity is exhausted
Vector<Integer> v3 = new Vector<>(20, 5);
// Copy constructor
Vector<Integer> v4 = new Vector<>(v2);
System.out.println("v1 capacity: " + v1.capacity()); // 10
System.out.println("v3 capacity: " + v3.capacity()); // 20
}
}
Output:
v1 capacity: 10
v3 capacity: 20
Note:
capacity()returns the current internal array size, which can be larger thansize()(the number of actual elements stored).
Common Methods
Vector inherits all List methods and adds a few legacy ones carried over from its pre-Collections days.
import java.util.Vector;
public class VectorMethods {
public static void main(String[] args) {
Vector<String> v = new Vector<>();
// Adding elements
v.add("Apple");
v.addElement("Banana"); // legacy alias for add()
v.add(1, "Mango"); // insert at index 1
System.out.println("List: " + v);
// Accessing elements
System.out.println("First: " + v.get(0));
System.out.println("elementAt(2): " + v.elementAt(2)); // legacy
// Updating
v.set(0, "Apricot");
System.out.println("After set: " + v);
// Removing
v.remove("Mango");
v.removeElementAt(1); // legacy alias
System.out.println("After remove: " + v);
// Searching
System.out.println("Contains Apricot: " + v.contains("Apricot"));
System.out.println("Index of Apricot: " + v.indexOf("Apricot"));
// Size and capacity
System.out.println("Size: " + v.size());
System.out.println("Empty: " + v.isEmpty());
}
}
Output:
List: [Apricot, Mango, Banana]
First: Apple
elementAt(2): Banana
After set: [Apricot, Mango, Banana]
After remove: [Apricot]
Contains Apricot: true
Index of Apricot: 0
Size: 1
Empty: false
Iterating a Vector
You can iterate using the modern for-each loop, an Iterator, or the classic Enumeration (legacy).
import java.util.Enumeration;
import java.util.Vector;
public class VectorIteration {
public static void main(String[] args) {
Vector<Integer> numbers = new Vector<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
// Modern for-each
System.out.print("for-each: ");
for (int n : numbers) {
System.out.print(n + " ");
}
// Legacy Enumeration
System.out.print("\nEnumeration: ");
Enumeration<Integer> e = numbers.elements();
while (e.hasMoreElements()) {
System.out.print(e.nextElement() + " ");
}
System.out.println();
}
}
Output:
for-each: 10 20 30
Enumeration: 10 20 30
Thread Safety in Action
Because all methods are synchronized, Vector is safe to share between threads without additional locking.
import java.util.Vector;
public class VectorThreadSafety {
public static void main(String[] args) throws InterruptedException {
Vector<Integer> shared = new Vector<>();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
shared.add(i);
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
// Always exactly 2000 — no data race possible
System.out.println("Final size: " + shared.size());
}
}
Output:
Final size: 2000
Warning: Synchronization at the method level does not make compound operations atomic. For example, a
check-then-actsequence likeif (!v.contains(x)) v.add(x)is still not thread-safe without an external lock, because another thread can sneak in between the two calls.
Under the Hood
Memory and Growth
Internally, Vector stores elements in an Object[] array. When you add an element and the array is full, Vector doubles the internal array by default. You can override this with the capacityIncrement constructor parameter — if you set it to, say, 5, the array grows by exactly 5 slots each time instead of doubling.
| Behavior | ArrayList | Vector |
|---|---|---|
| Default initial capacity | 10 | 10 |
| Growth strategy | 50% increase (oldCapacity + oldCapacity >> 1) | Double (or capacityIncrement if set) |
| Synchronization | None | Every public method |
How Synchronization Works
Each synchronized method in Vector acquires the intrinsic lock on the Vector object before executing. This is identical to marking the method with the synchronized keyword yourself. While safe, it means:
- One thread at a time can access the vector, even for reads.
- Under high contention, threads queue up waiting for the lock, causing measurable performance overhead compared to unsynchronized
ArrayList.
For scenarios where you need concurrent access but want better throughput, prefer java.util.concurrent.CopyOnWriteArrayList (from the concurrent collections) or wrap an ArrayList with Collections.synchronizedList().
Vector vs ArrayList
| Feature | Vector | ArrayList |
|---|---|---|
| Package | java.util | java.util |
| Thread-safe | Yes (method-level sync) | No |
| Growth strategy | Doubles (default) | ~50% increase |
Legacy API (elementAt, addElement) | Yes | No |
Enumeration support | Yes | No |
| Performance (single-threaded) | Slower (lock overhead) | Faster |
| Introduced in | Java 1.0 | Java 1.2 |
See the dedicated ArrayList vs Vector page for a deeper comparison.
Stack — A Subclass of Vector
java.util.Stack extends Vector and adds classic push/pop/peek semantics. Because it inherits all of Vector’s methods, you can accidentally call add(index, element) on a Stack, which breaks the LIFO contract. For a proper stack, prefer Deque/ArrayDeque.
import java.util.Stack;
public class StackDemo {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
stack.push("first");
stack.push("second");
stack.push("third");
System.out.println("Peek: " + stack.peek()); // third
System.out.println("Pop: " + stack.pop()); // third
System.out.println("Size: " + stack.size()); // 2
}
}
Output:
Peek: third
Pop: third
Size: 2
When Should You Use Vector?
Be intentional about reaching for Vector. Here is a quick decision guide:
- Legacy codebase that already uses Vector — keep it; changing it may introduce risk.
- You need a thread-safe list and simplicity matters —
VectororCollections.synchronizedList(new ArrayList<>())are fine for low-contention situations. - High-concurrency read-heavy workload — use
CopyOnWriteArrayListfrom concurrent collections. - Single-threaded code — always prefer
ArrayList; there is zero reason to pay the synchronization cost.
Tip: The Java documentation itself describes
Vectoras a legacy class and recommends usingArrayListfor non-concurrent use, andCopyOnWriteArrayListorCollections.synchronizedList()for concurrent use.
Related Topics
- ArrayList — the modern, unsynchronized alternative to Vector for most use cases
- ArrayList vs Vector — a detailed side-by-side comparison of both classes
- Stack —
java.util.Stack, which extends Vector with LIFO push/pop operations - Concurrent Collections — modern thread-safe alternatives like
CopyOnWriteArrayListandConcurrentHashMap - List Interface — the common interface that Vector, ArrayList, and LinkedList all implement
- Synchronization — how Java’s
synchronizedkeyword and intrinsic locks work under the hood