root@coding-prodigies:~# โ–Š
// lesson 3 of 11 ยท 18 min

The Collections framework

โš‘ Report an issue with this lesson
List<String> names = new ArrayList<>();
names.add("Ada");

Map<String, Integer> prices = new HashMap<>();
prices.put("html", 2999);

Set<String> uniqueTags = new HashSet<>();
uniqueTags.add("beginner");

Choose the right structure for the job: ArrayList for ordered, indexable data; HashMap for fast key lookups; HashSet when you only care whether something exists, not its order. LinkedList, TreeMap, and TreeSet exist for cases needing different ordering or insertion guarantees.

Know the actual complexity of what you're calling. ArrayList.get(i) is O(1) (backed by a real array), but ArrayList.contains(x) and .remove(x) (by value, not index) are O(n) -- they scan linearly. A HashMap/HashSet gives O(1) average-case lookup and insertion by hashing the key, but that guarantee quietly degrades to O(n) if many keys collide -- which is exactly why a mutable object used as a HashMap key is dangerous: if its hashCode() depends on fields that change after insertion, the map can no longer find it in the correct bucket, and lookups start silently failing. TreeMap/TreeSet trade that O(1) average case for guaranteed O(log n), backed by a red-black tree, in exchange for keeping entries sorted by key.

Beyond the basics, Deque (usually backed by ArrayDeque) covers both stack and queue behavior with push/pop/offer/poll, and is the recommended replacement for the legacy Stack class, which is needlessly synchronized (and therefore slower) for single-threaded use. PriorityQueue keeps its smallest (or, with a custom Comparator, "highest priority") element at the head, which makes it the natural structure for scheduling or any "always process the most urgent item next" problem:

Deque<Integer> stack = new ArrayDeque<>();
stack.push(1);
stack.push(2);
stack.pop(); // 2 -- last in, first out

PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.addAll(List.of(5, 1, 3));
pq.poll(); // 1 -- smallest first

One gotcha worth internalizing early: most of these collections are fail-fast. Modifying a list while iterating it directly with a for-each loop throws ConcurrentModificationException rather than silently producing a wrong result:

for (String name : names) {
    if (name.equals("Ada")) {
        names.remove(name); // throws ConcurrentModificationException
    }
}
// use an Iterator directly and call it.remove(), or collect matches
// into a separate list and remove them afterward, instead

Also remember that none of ArrayList, HashMap, or HashSet are thread-safe -- two threads mutating one concurrently can corrupt its internal structure, not just produce a race on the data. Reach for ConcurrentHashMap, CopyOnWriteArrayList, or external synchronization when a collection is shared across threads, covered in more depth in the concurrency lesson.

Try it yourself

Exercise: Create an ArrayList<String> named names containing "Ada", "Grace", "Alan", and a HashMap<String, Integer> named prices with "html" -> 2999. Print names.size() then prices.get("html").
Expected output:
3
2999
java
Output

      
    

Run your code and get it working before marking this lesson complete.

// that was the last free lesson

8 more lessons โ€” including Project: build a simple task scheduler โ€” and a final exam plus a certificate are waiting.

Unlock the full course โ€” $99.99