The Collections Framework

Collections Framework Overview

15 min Lesson 1 of 14

Collections Framework Overview

Before the Java Collections Framework (JCF) arrived in Java 2, developers had to build their own data structures from scratch or rely on the clunky Vector and Hashtable classes. The JCF changed everything: it provides a unified architecture for storing, retrieving, and manipulating groups of objects. Understanding its design — the interfaces, their relationships, and their intended uses — is the foundation for every lesson in this tutorial.

Why a Framework, Not Just Classes?

The JCF is built around interfaces, not concrete classes. Your variables and parameters should be typed to the interface (List, Set, Map) rather than the implementation (ArrayList, HashSet, HashMap). This lets you swap one implementation for another with a single-line change — a principle called programming to the interface.

Key design principle: Declare variables using the most general interface that meets your needs. Write List<String> names = new ArrayList<>();, not ArrayList<String> names = new ArrayList<>();. If you later decide LinkedList fits better, only one line changes.

The Interface Hierarchy

Everything starts with two root interfaces:

  • Iterable<T> — the top of the tree. Any class implementing Iterable can be used in a for-each loop. It declares one method: iterator().
  • Collection<T> — extends Iterable and adds the core operations: add(), remove(), size(), contains(), isEmpty(), and bulk operations like addAll() and removeAll().

Three major sub-interfaces extend Collection:

  • List<T> — an ordered sequence where duplicates are allowed and elements are accessed by integer index.
  • Set<T> — a collection with no duplicate elements; does not guarantee order (depends on the implementation).
  • Queue<T> — a collection designed for holding elements before processing, typically in FIFO order. Deque (double-ended queue) extends Queue.

Map<K, V> does not extend Collection. A map stores key-value pairs; each key is unique. It has its own interface hierarchy (SortedMap, NavigableMap).

A Quick Mental Model

Think of each interface as answering a different question about your data:

  • List — "I care about order and position. Give me the 3rd element."
  • Set — "I only care whether something is present. No duplicates."
  • Queue — "Process elements in a specific order (FIFO, priority, etc.)."
  • Map — "Look up a value by its key."

Concrete Implementations at a Glance

Every interface has several implementations tuned for different performance trade-offs. Here is a preview (each will be covered in its own lesson):

  • List: ArrayList (fast random access), LinkedList (fast insert/delete at ends)
  • Set: HashSet (O(1) contains), TreeSet (sorted), LinkedHashSet (insertion order)
  • Queue / Deque: ArrayDeque (fast stack/queue), PriorityQueue (heap-based)
  • Map: HashMap (O(1) lookup), TreeMap (sorted keys), LinkedHashMap (insertion order)

Your First Collections Code

Let us see the four main interfaces in action in a single runnable snippet:

import java.util.*; public class CollectionsOverview { public static void main(String[] args) { // List — ordered, duplicates allowed List<String> languages = new ArrayList<>(); languages.add("Java"); languages.add("Kotlin"); languages.add("Java"); // duplicate is fine System.out.println(languages); // [Java, Kotlin, Java] System.out.println(languages.get(1)); // Kotlin — index access // Set — no duplicates Set<String> unique = new HashSet<>(languages); System.out.println(unique); // [Java, Kotlin] — order not guaranteed // Queue — FIFO processing Queue<String> tasks = new ArrayDeque<>(); tasks.offer("compile"); tasks.offer("test"); tasks.offer("deploy"); System.out.println(tasks.poll()); // compile — removes head // Map — key-value lookup Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 95); scores.put("Bob", 87); System.out.println(scores.get("Alice")); // 95 System.out.println(scores.containsKey("Charlie")); // false } }
Import once: import java.util.*; covers every class and interface in the Collections Framework. For production code some teams prefer explicit imports for clarity, but the wildcard import is perfectly fine.

The Iterable/Collection Contract

Because List, Set, and Queue all ultimately implement Iterable, you can iterate over any of them with the same for-each loop — no need to know the concrete type:

// Works for ANY Collection — List, Set, Queue, etc. static void printAll(Collection<?> col) { for (Object item : col) { System.out.println(item); } }

This is the power of the hierarchy: write code once against an interface, and it works with every implementation, present and future.

Do not use raw types. Writing List list = new ArrayList(); (without generics) compiles but disables type safety and generates unchecked-cast warnings. Always supply the type parameter: List<String>.

Choosing the Right Interface

A practical decision tree before you code:

  1. Do you need key-value lookup? → Map
  2. Do you need uniqueness guarantees? → Set
  3. Do you need FIFO / priority processing? → Queue / PriorityQueue
  4. Do you need ordered access by index? → List

Picking the right interface from the start prevents bugs and makes your intent obvious to other developers reading your code.

Summary

The Java Collections Framework gives you a single, coherent architecture built on interfaces. Iterable and Collection sit at the root; List, Set, and Queue refine it for different access patterns; Map stands apart as a key-value store. Every concrete class (ArrayList, HashSet, HashMap, …) is just an implementation of one of these interfaces. Program to the interface — your code will be cleaner, more flexible, and easier to test. The next lesson dives deep into ArrayList, the most widely used List implementation.