The Birth of Reusable Containers
From hand-rolled arrays to Java 1.0's first container classes — Vector, Stack, Hashtable, Enumeration — and the lessons learned
The history of every mainstream language goes through the same arc:
arrays first, then containers. C had arrays in 1972 and the STL
arrived for C++ in the mid-1990s. Smalltalk had a rich container
library from the start. Python had list and dict baked into the
language. Java, when it first shipped in 1996, had four container
classes and one helper interface in java.util:
Vector— a growable array ofObjectStack— aVectorwithpush/popHashtable— a key-to-value map ofObjectProperties— aHashtableforStringpairsEnumeration— an interface for walking through them
That was it. For three years (Java 1.0, 1.1) those were the entire collection story. The story is worth telling because the next generation of containers — the Collections Framework — was designed specifically to fix what these classes got wrong.
Why containers had to come into the language
Three pressures forced the issue:
A function that takes "a list of things and gives you back the unique ones" should not need to know whether you passed it a hand-rolled growing array or a linked list. It should accept a container shape. That requires the container shapes to be named in the standard library, so every program in the world reaches for the same names.
Vector: the first growable list
Vector is the great-grandparent of ArrayList. It is a growable
array of Object, with addElement, removeElement,
elementAt(int), and a small army of legacy methods.
Read that carefully. There is no element type. A Vector of
customers and a Vector of integers are the same Java type. Whatever
comes out has to be downcast:
If a teammate accidentally adds an Integer, that cast blows up at
runtime, not at compile time. This is the bug class that generics
were eventually invented to kill.
Stack: a Vector with push and pop
Stack is a small subclass of Vector that adds push, pop,
peek, and empty. It is the textbook example of how the early
library used inheritance where it should have used composition (we'll
return to this — modern code prefers ArrayDeque for stack behavior).
Because Stack extends Vector, you can also call elementAt(0) on
it and reach the bottom of the stack, breaking the abstraction. That
is a famous wart — and a great example of why "is-a" inheritance is
a dangerous design tool to reach for first.
Hashtable: the first map
Hashtable is the great-grandparent of HashMap. It maps keys to
values using a hash function.
Same problems as Vector: untyped, and also synchronized on every
method. Every put and every get took a lock — useful in 1996
when threading was new, expensive every day after.
Enumeration: the original iterator
Enumeration<E> predates Iterator<E> and has only two methods:
hasMoreElements() and nextElement(). The for-each loop did not
exist yet; everyone wrote the while loop by hand.
It worked. It was uniform-ish. But it could not be used in the
for-each loop, it could not be removed during iteration, and there
was nothing standard above it — every container exposed its own
"walking" method (elements() for Vector, keys() and
elements() for Hashtable, etc.).
Lessons from the first generation
By 1997 the cracks were obvious:
| What the early containers did | What it cost |
|---|---|
One container interface (effectively, "container of Object") | No compile-time type safety; casts everywhere |
| Synchronization built into every method | Slow even when single-threaded |
Inheritance used for Stack extends Vector | Leaky abstractions you couldn't truly hide behind |
No shared interface like List or Map | A method that worked on one container shape couldn't be reused for another |
Enumeration rather than something more capable | No remove() during walks, no integration with future for-each |
Every one of those bullets becomes a design goal in the Java 1.2 Collections Framework — and a language-level fix in Java 5 generics.
The same idea, rewritten with a hand-rolled container
Just to feel what reusable containers look like, here is a tiny
generic-free generic-style container that mimics Vector. Notice
that even building one such thing is non-trivial — and now imagine
needing one for "list", "set", "map", "queue", "stack",
"linked list", "tree map", and so on.
This little Bag is essentially Vector minus the threading and the
ceremony. If every team in the world is going to need one anyway,
the language should ship it. That insight is what produced the
Collections Framework.
Test your understanding
Why does Vector.get(...) return Object and force a cast in pre-Java-5 code?
Because Object is faster than other types
Because Vector had no element-type parameter — the same class held any kind of element
Because Vector is an interface, not a class
Because Java compiles all collections to Object[] at runtime
What is wrong with Stack extends Vector?
It's not allowed by the compiler
It exposes Vector's index-based methods, breaking the LIFO abstraction a stack is supposed to provide
It causes a stack overflow at runtime
It prevents Stack from being iterated
Beyond type safety, what's a second major reason Hashtable is considered legacy?
It only supports Integer keys
Every method is synchronized, so it pays a locking cost even in single-threaded code
It cannot store more than 16 entries
It was removed in Java 9
The Tyranny of Arrays
How raw arrays and procedural data handling broke down as software grew, and why the industry needed reusable containers
The Collections Framework
Why Java 1.2 introduced a unified hierarchy of interfaces and implementations — and the shape of the framework you'll use for the rest of the course