StackTipsStackTips

Introduction to Java Collections

Java Collections API is a set of interfaces and implementations included in the Java standard library.

July 24, 2024 · 3 min read

The Java Collections API is a set of interfaces and implementations included in the Java standard library. The collections framework defines different data structures for storing, grouping, and retrieving objects.

Each implementation has its own trade-offs in ordering, performance, and thread-safety, so picking the right one matters more than people usually assume. All collection interfaces and classes live under the java.util package.

In this course, we'll walk through the most commonly used collections and learn how to perform the basic operations on each one.

Collection Interface

The Collection interface sits at the root of the collection hierarchy and defines the general-purpose behaviour shared across the hierarchy. It has no concrete implementation of its own.

List Interface

A List is an ordered collection of elements that may contain duplicates. Elements are indexed the same way as an array, starting at zero.

Elements can be inserted, accessed, and removed based on their index. The common List implementations in Java are:

Set Interface

A Set is a collection that doesn't allow duplicate entries. It exposes largely the same methods as List, but it can hold at most one null element.

The common Set implementations in Java include:

Queue Interface

A Queue represents a collection where elements are inserted at the back and removed from the front (FIFO — first in, first out).

The common Queue implementations include:

Map Interface

Map isn't technically a Collection, but it's still part of the Collections Framework. A Map stores data as key-value pairs, where each key maps to exactly one value.

The common Map implementations include:

  • HashMap: unordered, allows one null key and multiple null values.
  • LinkedHashMap: ordered by insertion, allows one null key and multiple null values.
  • IdentityHashMap: uses reference equality for keys.
  • TreeMap: sorted order, doesn't allow null keys, allows multiple null values.
  • Hashtable: synchronized, doesn't allow null keys or values.

Fail-Fast vs Fail-Safe Iterators

Every non-concurrent collection in this course — ArrayList, HashMap, HashSet, TreeMap, TreeSet, and so on — tracks a modCount field that increments on every structural change (add/remove, not set()). Their iterators capture modCount at creation and compare it on every next() call. If the two don't match, you get a ConcurrentModificationException. This is called fail-fast: it exists to catch bugs, not to provide thread safety — it's a best-effort detector, not a guarantee, and it won't reliably trigger under genuine concurrent access.

Two implementations in this course opt out of that behaviour:

  • CopyOnWriteArrayList iterates over a private snapshot of the array taken when the iterator was created. It never throws ConcurrentModificationException, and it won't see mutations made after the iterator was created either.
  • DelayQueue uses a weakly consistent iterator — it also won't throw, and it may or may not reflect concurrent changes, with no guarantee either way.

The practical rule: never call list.remove(item) inside a for-each loop over list. Use Iterator.remove(), ListIterator, or removeIf() instead — all three update modCount in a way the iterator already accounts for.

Choosing a Collection

You needReach for
An indexable, resizable listArrayList
Frequent inserts/removals at both endsArrayDeque
A thread-safe list you mostly read, rarely writeCopyOnWriteArrayList
Unique elements, no ordering guaranteesHashSet
Unique elements, insertion order preservedLinkedHashSet
Unique elements, sorted orderTreeSet
Fast key lookups, no orderingHashMap
Fast key lookups, insertion (or access) order preservedLinkedHashMap
Fast key lookups, sorted by keyTreeMap
Always process the smallest/largest element nextPriorityQueue
Process elements only after a delay expiresDelayQueue
A LIFO stackArrayDeque (not Stack)

Legacy classes worth knowing but avoiding in new code: Vector, Stack, and Hashtable. They predate the Collections Framework, use coarse-grained per-method synchronization that hurts single-threaded performance for no benefit, and have modern replacements that are either faster (ArrayDeque) or genuinely thread-safe under concurrent access (ConcurrentHashMap, CopyOnWriteArrayList).


Related articles