RizTech Academy logo
RizTech Academy
CollectionsLesson 1 of 720 min

The Collections framework, mapped out

The arrays lesson ended with a list of things arrays are not for: anything that grows, anything looked up by key, anything you check membership in. The Collections framework is the answer to all three, and it is the part of the standard library you will use every working day.

This lesson is the map. The next five are the territory.

The three shapes

Almost everything reduces to three questions about what you are storing.

Question Answer Java type
An ordered sequence, duplicates allowed Position matters List
A bag of unique things, order irrelevant "Have I seen this?" Set
Things found by a key "What is the value for X?" Map
List<String> deliveryOrder = new ArrayList<>();   // Priya, then Arjun, then Kavita
Set<String> areasServed = new HashSet<>();        // Wagholi, Kharadi — each once
Map<String, Integer> tiffinsByName = new HashMap<>();  // "Priya" -> 26

Choosing wrongly is the most common design mistake in beginner Java. A List used for membership checks is slow and full of duplicates; a Map used for an ordered sequence loses the order. Getting this right is worth more than knowing every method on ArrayList.

The hierarchy, only as much as you need

                 Iterable
                    |
                Collection ───────────────┐
                 /      \                 │
              List      Set            (Map is NOT a Collection)
               |         |                 |
          ArrayList   HashSet          HashMap
          LinkedList  LinkedHashSet    LinkedHashMap
                      TreeSet          TreeMap

Two things to take from that picture.

Map is not a Collection. It has no add, it is not Iterable, and you cannot pass one where a Collection is expected. It holds pairs, which is a different shape. You iterate its entrySet(), keySet() or values(), each of which is a collection.

Iterable is what the enhanced for loop needs. Anything implementing it works with for (T x : thing), including your own classes.

The implementations you will actually use

Interface Implementation Ordering When
List ArrayList Insertion The default. Fast access by index
List LinkedList Insertion Rarely. Queue or deque behaviour
Set HashSet None The default. Fast membership
Set LinkedHashSet Insertion When you need order and uniqueness
Set TreeSet Sorted When you need sorted order maintained
Map HashMap None The default. Fast lookup
Map LinkedHashMap Insertion Predictable iteration, caches
Map TreeMap Sorted by key Ranges, sorted reports
Deque ArrayDeque Insertion Stacks and queues

Start with ArrayList, HashSet and HashMap. Move off them only for a reason you can state. LinkedList in particular is almost always the wrong answer despite its prominence in textbooks — the choosing lesson at the end of this module measures it.

Declare the interface, create the implementation

List<String> names = new ArrayList<>();
Map<String, Integer> counts = new HashMap<>();

Not ArrayList<String> names = new ArrayList<>(). The variable's type is the promise you make to the rest of the code; the implementation is a decision you might change. Swapping HashMap for LinkedHashMap should be a one-word edit, and it is only if nothing depends on the concrete type.

The same applies to parameters and return types. A method taking List<String> can be called with any list; one taking ArrayList<String> cannot.

Creating them

Four ways, with different results — and the differences catch people.

// 1. Empty and growable
List<String> a = new ArrayList<>();

// 2. Immutable, from Java 9. Cannot add, remove or set. Rejects nulls.
List<String> b = List.of("Priya", "Arjun");

// 3. A fixed-size view of an array. set() works, add() does not.
List<String> c = Arrays.asList(array);

// 4. A growable copy of any of the above
List<String> d = new ArrayList<>(b);
List.of("Priya").add("Arjun");
UnsupportedOperationException

List.of, Set.of and Map.of are immutable. That is usually a feature — use them for constants and for anything you hand out — but it is the commonest surprise in modern Java, because the exception mentions nothing about immutability.

Arrays.asList is stranger still: it is a view of the array, so set writes through to the array and add throws.

String[] arr = {"Priya", "Arjun"};
List<String> view = Arrays.asList(arr);
view.set(0, "Kavita");
System.out.println(Arrays.toString(arr));
[Kavita, Arjun]

When you want something you can modify freely, wrap it: new ArrayList<>(...).

Methods every collection has

From Collection, so they work on both List and Set:

Method Does
add(e) Adds. Returns false on a Set if already present
remove(o) Removes one matching element
contains(o) Membership, using equals
size() Count
isEmpty() Count is zero
clear() Removes everything
addAll(c) / removeAll(c) / retainAll(c) Bulk operations
removeIf(predicate) Removes everything matching
forEach(action) Applies an action to each
stream() Opens module 6
iterator() The manual way to walk it

Note contains uses equals, not ==. Which means it only works for your own classes if you have written equals and hashCode correctly — the lesson later in this module that exists because getting it wrong produces silent duplicates.

Iterating

for (String name : names) { ... }              // usual
names.forEach(name -> { ... });                // module 6 style
for (int i = 0; i < names.size(); i++) { ... } // only when you need the index

for (Map.Entry<String, Integer> e : counts.entrySet()) {
    System.out.println(e.getKey() + " = " + e.getValue());
}

For a map, iterate entrySet() when you need both halves. Iterating keySet() and calling get inside the loop does the lookup work twice.

And one rule that has its own section in the next lesson: do not modify a collection while a for loop is walking it. The consequences are inconsistent enough to be genuinely dangerous.

What the framework is not for

  • Primitives without boxing. List<Integer> stores objects. For millions of numbers that costs memory and speed; use an array or IntStream.
  • Thread safety. ArrayList and HashMap are not safe for concurrent modification. Module 9 covers what to use instead — and the answer is not Vector or Hashtable, which are obsolete.
  • Sorted order for free. A HashMap has no order, and the order it happens to show is not insertion order and may change between runs and versions. Never rely on it.

Check your work

What are the three shapes and their questions? List — position matters, duplicates allowed. Set — have I seen this before. Map — what is the value for this key.

Why is Map not a Collection? It holds pairs rather than elements. It has no add, is not Iterable, and exposes entrySet(), keySet() and values() when you want a collection view.

Why declare List rather than ArrayList? The interface is the promise; the implementation is a decision you may change. Depending on the concrete type makes swapping it a wide edit.

What does List.of(...).add(...) do? Throws UnsupportedOperationException. The factory methods produce immutable collections.

How does Arrays.asList differ from List.of? It is a fixed-size view of the array — set writes through to the array, add throws. List.of is a fully immutable copy that also rejects nulls.

Which three implementations are the defaults? ArrayList, HashSet, HashMap. Move off them only for a reason you can state.

Why iterate entrySet() rather than keySet()? Iterating keys and calling get inside the loop performs the lookup twice for every entry.

Practice 3, the right shape for each. Delivery order for a round is a List — sequence matters and the same customer could appear twice on a split round. Areas served is a Set — each once, membership is the question. Tiffins per customer is a Map. Customers per area is a Map<String, List<String>>, and the next lesson but one shows the one-line way to build it.

Practice

  1. Create all four kinds of list — new ArrayList<>(), List.of(...), Arrays.asList(array) and new ArrayList<>(List.of(...)). Try add and set on each and note which throw.

  2. Prove Arrays.asList is a view. Build one from an array, set an element, then print the original array.

  3. Pick the shape. For each, name the type and say why in one clause: the order tiffins are delivered on a round; the areas your service covers; how many tiffins each customer took this month; which customers live in each area; whether a pincode is serviceable; the last ten complaints in the order they arrived.

  4. Swap the implementation. Write a method taking List<String>, call it with an ArrayList and then with List.of(...). Then change the parameter to ArrayList<String> and see which call stops compiling.

  5. Iterate a map both ways. Once over keySet() with get inside, once over entrySet(). Then put a print statement inside get — you cannot, so instead reason about how many lookups each version performs for a map of 1,000 entries.

  6. Harder — a delivery round. Build a Map<String, List<String>> of area to customer names from a flat list of "Priya,Wagholi" lines. Print each area with its customers, areas in alphabetical order and customers in the order they appeared. Choosing the right map type for each of those two requirements is the exercise.

Next: List and ArrayList in detail, and the removal that behaves three different ways.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship