Java Vector
Table of Contents + −
In the last lesson you learned about Java LinkedList. Now we meet an older cousin of ArrayList called the Vector. It works almost exactly like an ArrayList but is thread-safe, and that safety comes at a price. It is a class you should be able to read but rarely choose to write.
🤔 The problem Vector tried to solve
Imagine two parts of your program running at the same time. One adds items to a list. The other reads from that same list at that exact moment. This is called concurrency. When two threads touch one list together, things go wrong:
- One can see a half-finished change.
- The list can get corrupted.
- Your program can crash or give wrong answers.
Early Java wanted a list safe for many threads out of the box, so they made Vector. Every method is synchronized, meaning only one thread runs it at a time. The others wait their turn. So two threads can never scramble the list together.
The catch is the waiting. That safety has a real cost, and for almost every modern program there is a better option.
🧩 Vector is a List, just like ArrayList
A Vector implements the same List interface as ArrayList. So all the methods you know work here too. Import it, create it with a type, and use add, get, set, remove, and size.
import java.util.Vector;
public class Main { public static void main(String[] args) { Vector<String> fruits = new Vector<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Cherry");
System.out.println(fruits.get(1)); // ✅ same get as ArrayList fruits.set(0, "Apricot"); // replace index 0 fruits.remove("Cherry"); // remove by value
System.out.println("Size: " + fruits.size()); System.out.println(fruits); }}What each line does:
add("Apple")puts a new item at the back, like ArrayList.get(1)returns the second item,Banana.set(0, "Apricot")replaces the item at index 0.remove("Cherry")deletes the first match by value.size()reports how many items remain.
This is identical to ArrayList code. Only the type name changed. That is the point of the List interface.
Output
BananaSize: 2[Apricot, Banana]📜 The legacy methods
Vector is older than the Collections framework, so it carries legacy methods with long, old-style names. Legacy means “kept around from the old days for backward compatibility”. They do the same jobs as the modern methods, with different names.
The legacy methods you will run into:
addElement(x)adds an item to the end, same asadd(x).elementAt(i)reads the item at an index, same asget(i).firstElement()andlastElement()read the first and last items.removeElement(x)removes by value, same asremove(x).elementCountis the old field behindsize().
This example mixes old names with modern ones so you can see they do the same work.
import java.util.Vector;
public class Main { public static void main(String[] args) { Vector<String> names = new Vector<>(); names.addElement("Alex"); // ✅ legacy way to add names.addElement("Riya"); names.add("Arjun"); // ✅ modern way, same result
System.out.println(names.elementAt(0)); // legacy get System.out.println(names.get(2)); // modern get System.out.println("First: " + names.firstElement()); System.out.println("Last: " + names.lastElement()); }}The walkthrough:
addElementandaddboth append to the end.elementAt(0)andget(2)both read by index, just with different names.firstElementandlastElementare shortcuts for the two ends.
Prefer the modern names. The legacy ones still work, but add and get match every other List, so your code reads consistently. The legacy names exist mainly so very old code keeps compiling.
Output
AlexArjunFirst: AlexLast: Riya🔒 What “synchronized” really buys you
Every public method on Vector is synchronized. So when one thread is inside add, no other thread can enter add, get, remove, or any method. They line up and wait, which stops two threads from corrupting the list at once.
This example adds items from the main thread. The synchronization is invisible because only one thread runs, but it happens on every call.
import java.util.Vector;
public class Main { public static void main(String[] args) { Vector<Integer> scores = new Vector<>();
// Every add() below locks the Vector, then unlocks it. for (int i = 1; i <= 5; i++) { scores.add(i * 10); }
System.out.println(scores); System.out.println("Total items: " + scores.size()); }}Notice the hidden cost:
- Each
addquietly locks the whole Vector, runs, then unlocks it. - That lock and unlock happens five times here, once per call.
- In a single-threaded program, the locking protects nothing. You pay the cost for zero benefit.
That is why Vector fell out of favor. Most code runs on one thread for a given list, so all that locking is wasted work.
Output
[10, 20, 30, 40, 50]Total items: 5⚠️ Synchronized methods are not the same as safe
Each Vector method is safe on its own. But putting two methods together is not. A sequence of two calls is a compound operation, and Vector does not protect the gap between them. Look at this classic check-then-act bug.
// ❌ Looks safe, but it is not, with many threadsif (!vector.isEmpty()) { // thread A checks: not empty vector.remove(0); // another thread removed it first!}The danger:
- Thread A calls
isEmpty()and sees one item. - Before A runs the next line, thread B removes that item.
- A calls
remove(0)on an empty Vector and crashes.
Each call locked correctly, but the lock was released between them. “Each method is thread-safe” does not mean “any group of methods is thread-safe”. For grouped steps you need your own lock around the whole block.
Per-method safety is not enough
Vector locks each method, not your logic. Any time you do “check, then act” or “read, then update” across more than one call, you must wrap the whole sequence in your own synchronized block. The built-in locking will not save you there.
⚖️ ArrayList vs Vector
When do you pick Vector over ArrayList? Almost never. They store data the same way. The differences are about locking and age.
| Feature | ArrayList | Vector |
|---|---|---|
| Thread-safe by default | No | Yes (every method synchronized) |
| Speed in single-thread code | Fast (no locking) | Slower (locks on every call) |
| How it grows when full | Grows by about half | Doubles in size |
| Age | Modern (Java 1.2) | Legacy (Java 1.0) |
| Legacy methods (elementAt, addElement) | No | Yes |
| Recommended for new code | Yes | No |
The simple guidance: for single-threaded code, which is most code, use ArrayList. It does the same job without the locking tax. Only think about thread safety when several threads truly share one list, and even then there are better tools than Vector.
🧵 What to use instead for real concurrency
If many threads genuinely share a list, Vector is still not your best choice. Modern Java gives you two cleaner options:
Collections.synchronizedList(new ArrayList<>())wraps an ArrayList in a synchronized shell. It behaves like Vector, but it is the standard modern way and it is clearer about what it does.CopyOnWriteArrayListis built for lists that are read often but written rarely. Reads need no lock at all, so they are very fast. Writes make a fresh copy behind the scenes.
This example shows both modern choices.
import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.concurrent.CopyOnWriteArrayList;
public class Main { public static void main(String[] args) { // ✅ synchronized wrapper around a normal ArrayList List<String> safe = Collections.synchronizedList(new ArrayList<>()); safe.add("shared item");
// ✅ great when reads happen far more often than writes List<String> readHeavy = new CopyOnWriteArrayList<>(); readHeavy.add("config value");
System.out.println(safe); System.out.println(readHeavy); }}Choosing between them:
- Use
Collections.synchronizedListfor a general thread-safe list with mixed reads and writes. - Use
CopyOnWriteArrayListwhen threads mostly read and rarely write, like a list of settings.
Both say clearly “this list is for concurrency”. Vector hides that intent and drags its legacy baggage along. In new code, reach for these.
Even these need care for compound steps
The same compound-operation rule applies. Collections.synchronizedList locks each call, not your logic. When you iterate over it, you must still wrap the loop in a synchronized block on the list. The single-call safety does not cover multi-call sequences.
📚 A quick note on Stack
One more class sits right on top of Vector. The Stack class extends Vector. A stack is a “last in, first out” pile. You push an item on top and pop the top off, like a stack of plates.
import java.util.Stack;
public class Main { public static void main(String[] args) { Stack<String> plates = new Stack<>(); plates.push("Plate 1"); plates.push("Plate 2"); plates.push("Plate 3");
System.out.println("Top: " + plates.peek()); // look at top System.out.println("Removed: " + plates.pop()); // take the top off System.out.println(plates); }}Quick read:
pushputs a plate on top of the pile.peeklooks at the top plate without taking it.popremoves and returns the top plate.
Plate 3 comes off first. That is last in, first out. But because Stack extends Vector, it carries the same locking cost. For new stack code, use ArrayDeque instead. It is faster and built for this job. Stack, like Vector, is mostly legacy.
Prefer ArrayDeque for stacks
For a modern stack, use ArrayDeque with push and pop. It is faster than Stack because it does not lock on every call, and it does not inherit Vector’s old design.
⚠️ Common Mistakes
A few Vector mix-ups show up over and over.
Using Vector by default. “Thread-safe” sounds safer, but most lists live on one thread. So the locking just slows you down for no gain.
// ❌ Vector in single-threaded code: paying for locks you never useVector<String> list = new Vector<>();
// ✅ ArrayList: same job, no locking taxList<String> list2 = new ArrayList<>();For everyday, single-thread code, ArrayList is the right default.
Assuming “thread-safe” means safe for everything. Each method is safe, but a sequence of methods is not. So check-then-act and read-then-update still need your own synchronized block around the whole sequence.
// ❌ unsafe across threads even though each call is synchronizedif (!vector.isEmpty()) { vector.remove(0);}
// ✅ lock the whole sequence yourselfsynchronized (vector) { if (!vector.isEmpty()) { vector.remove(0); }}Forgetting the import. You need import java.util.Vector; before using it. Leaving it out gives a “cannot find symbol” error.
✅ Best Practices
Build these habits and you will use the right list every time.
- Default to ArrayList. It fits almost every program and skips the locking cost.
- Avoid Vector in new code. Know how to read it, but do not reach for it.
- For real concurrency, use the modern tools. Pick
Collections.synchronizedListorCopyOnWriteArrayList, not Vector. - Lock compound operations yourself. Wrap “check then act” and “read then update” in your own
synchronizedblock. - Use ArrayDeque for stacks. It beats the legacy
Stackclass for new code. - Program to the List type. Declaring
List<String>instead ofVector<String>lets you swap the implementation later without touching the rest of your code.
🧩 What You’ve Learned
Nicely done. Let’s recap Vector in plain words.
- ✅ A Vector is a resizable-array List, just like ArrayList, but every method is synchronized.
- ✅ It supports the same List methods (
add,get,set,remove,size) plus legacy ones likeelementAtandaddElement. - ✅ The synchronization makes it slow in single-thread code, because it locks on every call for no benefit.
- ✅ Synchronized methods are not enough for compound operations. Group steps still need your own lock.
- ✅ For real concurrency, prefer
Collections.synchronizedListorCopyOnWriteArrayList. - ✅
Stackextends Vector, so it carries the same cost. UseArrayDequefor new stack code. - ✅ For most everyday work,
ArrayListis the better default.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
How is Vector different from ArrayList?
Why: Vector behaves like ArrayList but synchronizes every method, which makes it thread-safe and slower.
- 2
Why is Vector mostly considered legacy now?
Why: Most lists run on one thread, so the per-call locking is wasted work.
- 3
Does Vector's synchronization make a check-then-act sequence safe?
Why: Each method is safe alone, but the gap between two calls is not. Wrap compound steps in your own synchronized block.
- 4
What should you use for a thread-safe list in modern code?
Why: These are the modern, clearer tools for concurrency. Vector is legacy.
🚀 What’s Next?
Lists let you store items in order and allow duplicates. But sometimes you need a collection that keeps only unique items, with no repeats at all. That is a Set. Let’s learn how the Set interface works and why it is so useful.