ArrayList and Vector are both resizable-array implementations of List, but Vector synchronizes every method and doubles its capacity when full, while ArrayList has no locking and grows by about half. In the ArrayList vs Vector choice, Vector is a legacy class from Java 1.0, so new code uses ArrayList, and when several threads share a list, it uses Collections.synchronizedList() or CopyOnWriteArrayList instead of Vector.
The following example fills both classes with 11 sensor readings and shows the one difference we can observe from outside, the capacity.
List<Integer> readings = new ArrayList<>(); // no locking
Vector<Integer> legacy = new Vector<>(); // every method synchronized
for (int i = 0; i < 11; i++) {
readings.add(i);
legacy.add(i);
}
int vectorCapacity = legacy.capacity(); // 20, doubled from 10
int listSize = readings.size(); // 11, ArrayList has no capacity() method
List<Integer> shared = Collections.synchronizedList(new ArrayList<>()); // the modern thread-safe choice
Notice that Vector exposes capacity() while ArrayList hides it. We compare the two classes in a table, look at what Vector‘s locking does and does not protect, compare growth and iteration, and finish with the modern replacement for each Vector use case.
1. Differences Between ArrayList and Vector
Both classes store elements in an Object[] array, keep insertion order, allow duplicates and null, and give constant-time access by index. The differences come from their age, because Vector existed before the Collections Framework and was retrofitted to implement List in Java 1.2.

| Feature | ArrayList | Vector |
|---|---|---|
| Since | Java 1.2 | Java 1.0 (legacy) |
| Synchronization | None | Every public method locks the Vector |
| Growth when full | About 50% (10, 15, 22, 33 on JDK 25) | 100% by default, or a fixed capacityIncrement |
| Capacity visible | No | Yes, capacity() |
| Iterator | Fail-fast | Fail-fast |
| Enumeration | None | elements(), not fail-fast |
| Extra legacy methods | None | addElement(), elementAt(), removeElement() and others |
| Subclass | None in the JDK | Stack |
| Use in new code | Default List | Only for legacy APIs that require it |
When we do not need a thread-safe implementation, Vector is replaced by ArrayList, which does the same job without the locking.
2. Synchronization in Vector
Every public method of Vector that reads or changes elements runs under the synchronized lock of the Vector object, so a thread must hold that lock for each call. Two threads calling add() at the same time therefore cannot corrupt the array, which can happen with a plain ArrayList.
Locking each method does not make a sequence of calls atomic. A check followed by an action, such as “add this device if it is not in the list yet”, releases the lock between contains() and add(), so another thread can run in between.
static void registerUnsafe(Vector<String> devices, String id) {
if (!devices.contains(id)) { // threads A and B both see false
devices.add(id); // both add the same id
}
}
For example, a smart-home hub receives two “device online” messages for the same thermostat on two threads within a millisecond. With registerUnsafe(), the thermostat can end up twice in the list, and the dashboard shows two tiles for it. We need a lock around the whole check-and-add sequence, or a method that does both steps atomically.
static void registerSafe(List<String> devices, String id) {
synchronized (devices) {
if (!devices.contains(id)) {
devices.add(id);
}
}
}
List<String> devices = Collections.synchronizedList(new ArrayList<>());
registerSafe(devices, "thermostat");
registerSafe(devices, "thermostat");
int registered = devices.size(); // 1
CopyOnWriteArrayList<String> listeners = new CopyOnWriteArrayList<>();
boolean first = listeners.addIfAbsent("thermostat"); // true
boolean second = listeners.addIfAbsent("thermostat"); // false, already present
The synchronized block works the same with a Vector, a synchronized wrapper or any other list, as long as every thread locks on the same object. So the locking built into Vector gives no extra safety over Collections.synchronizedList(), and it costs a lock on every call even in single-threaded code. The tutorial on synchronizing an ArrayList covers both modern options in detail.
3. Capacity Growth
When the backing array is full, Vector doubles its capacity by default, so a default Vector goes 10, 20, 40, 80. On JDK 25, an ArrayList grows by half its capacity, going 10, 15, 22, 33. Doubling means fewer copies for a fast-growing list but more unused slots, up to half the array right after a resize.
A Vector can grow by a fixed step instead. The constructor Vector(int initialCapacity, int capacityIncrement) adds capacityIncrement slots on each resize, while ArrayList only lets us choose the initial capacity.
Vector<Integer> stepped = new Vector<>(10, 5);
for (int i = 0; i < 11; i++) {
stepped.add(i);
}
int steppedCapacity = stepped.capacity(); // 15, grew by the fixed increment
Both classes have ensureCapacity() to reserve room before a large batch and trimToSize() to drop unused slots. A small fixed increment on a large Vector causes many resizes, because each one copies the whole array to add only a few slots.
4. Iterator vs Enumeration
Both classes return fail-fast iterators from iterator() and listIterator(). When the list changes structurally outside the iterator, the next call to next() throws ConcurrentModificationException on a best-effort basis.
static void addDuringLoop(List<String> sensors) {
Iterator<String> it = sensors.iterator();
while (it.hasNext()) {
if (it.next().equals("attic")) {
sensors.add("yard");
}
}
}
addDuringLoop(new ArrayList<>(List.of("hall", "attic", "porch"))); // ConcurrentModificationException
addDuringLoop(new Vector<>(List.of("hall", "attic", "porch"))); // ConcurrentModificationException
The older Enumeration from Vector.elements() is not fail-fast. It keeps going after a change, so the loop sees the new element, and the results of such a loop are undefined.
Vector<String> sensors = new Vector<>(List.of("hall", "attic", "porch"));
Enumeration<String> names = sensors.elements();
StringBuilder seen = new StringBuilder();
while (names.hasMoreElements()) {
String name = names.nextElement();
if (name.equals("attic")) {
sensors.add("yard");
}
seen.append(name).append(' ');
}
String visited = seen.toString().strip(); // "hall attic porch yard", no exception
A missing exception is not a feature here. A fail-fast iterator reports the bug on the spot, whereas the Enumeration loop can skip or repeat elements when another thread removes one. To change a list while looping, we use removeIf() or the iterator’s own remove() on either class.
5. Replacing Vector in Modern Code
Code that still uses Vector does so for one of a few reasons, and each one has a modern replacement in java.util or java.util.concurrent.
| Old code | Modern replacement | Why |
|---|---|---|
| Vector as a general list | ArrayList | No lock on every call |
| Vector shared between threads | Collections.synchronizedList() or CopyOnWriteArrayList | Same locking, or lock-free reads |
| Stack | ArrayDeque with push() and pop() | Recommended by the Stack Javadoc |
| Enumeration loop | Enhanced for loop over the list, or asIterator() (Java 9) when only an Enumeration is available | The list’s own iterator is fail-fast; asIterator() only adapts the old type |
| elementAt(i), addElement(e) | get(i), add(e) | Standard List methods |
Converting between the two classes is one constructor call, because each one accepts any Collection. Collections.list() turns an Enumeration into an ArrayList when an old API returns one.
Vector<String> old = new Vector<>(List.of("hall", "attic"));
List<String> modern = new ArrayList<>(old); // [hall, attic]
Vector<String> forLegacyApi = new Vector<>(modern); // [hall, attic]
ArrayList<String> fromEnumeration = Collections.list(old.elements()); // [hall, attic]
Deque<String> undo = new ArrayDeque<>();
undo.push("type");
undo.push("bold");
String lastAction = undo.pop(); // "bold"
For example, Swing’s DefaultTableModel still takes Vector arguments in some constructors. We keep the data in an ArrayList everywhere else and convert with new Vector<>(rows) only at that call, so the legacy type stays at the edge of the app. For stacks and queues, the ArrayDeque tutorial shows the full API.
6. ArrayList vs Vector FAQs
Developers ask about Vector mostly while reading or migrating older code.
6.1. Is Vector Deprecated in Java?
No. Vector is not annotated with @Deprecated in Java 25, and it still works. It is a legacy class, and ArrayList replaces it when thread safety is not needed.
6.2. Is Vector Thread-Safe?
Each single method call is thread-safe, but sequences of calls are not. Check-and-add code and iteration need an external synchronized block on the Vector, as we saw in section 2.
6.3. Which Is Faster, ArrayList or Vector?
ArrayList does less work per call because it takes no lock. The gap depends on the JVM and on contention, so we measure with JMH before quoting numbers. When many threads share a list, the bigger cost is threads waiting for each other on the single lock, which affects Vector and Collections.synchronizedList() alike.
6.4. Should We Use Stack for a Stack?
No. Stack extends Vector, so it inherits the per-call locking and list methods that let code insert in the middle of a stack. The Deque interface replaces it, and ArrayDeque is the standard implementation for it.
6.5. Can ArrayList and Vector Store null?
Yes, both allow null elements and duplicates. The concurrent alternative CopyOnWriteArrayList allows null too, while List.of() rejects it.
7. Conclusion
ArrayList and Vector store data the same way, but Vector locks every call and doubles its capacity, while ArrayList takes no lock and grows by about half. The per-call locking does not protect check-and-add code or iteration, so it rarely gives the safety people expect from it.
In new code, we use ArrayList for single-threaded lists, Collections.synchronizedList() or CopyOnWriteArrayList for shared ones and ArrayDeque instead of Stack. Vector appears only where an old API demands it, converted at the boundary.
8. References
- Vector Javadoc (Java 25)
- ArrayList Javadoc (Java 25)
- Stack Javadoc (Java 25)
- Enumeration Javadoc (Java 25)
Happy Learning !!