Understanding Java Collections: LinkedList, ArrayList, Stack, Queue, HashMap, and LinkedHashMap with Animations and Code Examples
This article systematically explains common Java collection classes—including LinkedList, ArrayList, Stack, Queue, HashMap, and LinkedHashMap—highlighting their internal structures, key operations, performance characteristics, and JDK 8 differences, and it provides animated illustrations and full code snippets for each concept.
Recently I organized my knowledge of data structures and, after reviewing the most common Java collections, decided to create animations that visualize their data flow processes.
Based on JDK 8, some internal details differ from earlier versions: the doubly‑linked list used in LinkedList and LinkedHashMap is no longer circular, and HashMap now inserts new nodes at the tail of its singly‑linked buckets.
LinkedList is a classic doubly‑linked list suitable for unordered insertions and deletions; sequential access is slower than ArrayList . Operations such as add(E) / addLast(E) are illustrated with an animated GIF.
The add(index, E) method first checks whether the index is closer to the head or the tail to decide the traversal direction. The relevant source code is shown below:
1 if (index < (size >> 1)) {
2 Node
x = first;
3 for (int i = 0; i < index; i++)
4 x = x.next;
5 return x;
6 } else {
7 Node
x = last;
8 for (int i = size - 1; i > index; i--)
9 x = x.prev;
10 return x;
11 }Similarly, get(index) also chooses a traversal direction based on the index, but its performance remains sub‑optimal, which is why iterating a LinkedList with a plain for loop is discouraged in favor of an Iterator .
The remove(E) operation is demonstrated with another animated GIF.
ArrayList is backed by a resizable array, offering fast indexed reads but slower unordered insertions and deletions because elements must be shifted. The add(index, E) animation shows how elements are moved, and the resizing policy (default capacity 10, growth factor 1.5) is illustrated.
Removing an element from an ArrayList involves scanning the array and checking each element with equals , which is generally slower than the corresponding LinkedList removal.
Stack is a classic LIFO structure implemented on top of Vector (an array). The default capacity is 10 and it expands automatically. Push and pop operations are visualized with GIFs.
Postfix Expression demonstrates a typical use of a stack: converting an infix expression such as 9 + (3 - 1) * 3 + 10 / 2 to postfix notation and then evaluating it. The conversion rules (output numbers, handle operators, parentheses, and precedence) are listed in a bullet list, followed by an illustration of the process.
Queue differs from a stack in that removal occurs at the head while insertion occurs at the tail. The article introduces ArrayBlockingQueue , a bounded blocking queue commonly used in producer‑consumer scenarios. The put(E) method blocks when the queue is full; its implementation with a ReentrantLock is shown:
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}The take() method removes an element without shifting the underlying array; instead, it advances a circular takeIndex pointer. The corresponding dequeue code is provided:
private E dequeue() {
final Object[] items = this.items;
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}HashMap is the most widely used hash table in Java. It stores entries in an array of buckets, each bucket being a singly‑linked list; since JDK 8, buckets with more than eight nodes are transformed into red‑black trees (not covered here). The article explains the put(K,V) operation and the resize mechanism (doubling the array length). The resize algorithm that splits each old bucket into two new buckets based on the new high‑order bit is shown in code:
Node
loHead = null, loTail = null;
Node
hiHead = null, hiTail = null;
Node
next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null) loHead = e;
else loTail.next = e;
loTail = e;
} else {
if (hiTail == null) hiHead = e;
else hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}LinkedHashMap extends HashMap by maintaining a doubly‑linked list that preserves insertion order or access order (LRU) depending on the accessOrder flag. The article shows how put(K,V) , get(K) , and removeEldestEntry work, with diagrams illustrating the reordering of entries when accessOrder is true.
Finally, the author invites readers to like and share the post if they found the material useful.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.