Learn / Programming / Java / Collections Framework

Java · Lesson 5 of 6

Collections Framework

ArrayList, HashMap, and when to pick each.

  • Intermediate
  • 17 min read
  • 3 objectives

Before this lessonLesson 4: Classes and Objects

What you will learn

  • Use ArrayList add/get
  • Store key-value in HashMap
  • Iterate with for-each

Arrays have a fixed size. Real programs need data structures that grow, shrink and search efficiently, and Java's Collections Framework provides them in java.util. The three interfaces to know are List (ordered, duplicates allowed), Set (no duplicates) and Map (key-value pairs).

Arrays are fixed, collections grow

A plain array has a size that never changes. Real programs constantly add and remove items, look things up and remove duplicates. The Java Collections Framework provides ready-made containers for these jobs. The three you will use most: List (ordered, allows duplicates), Set (no duplicates) and Map (key to value, like a dictionary).

List and ArrayList

ArrayList is the default list. The angle brackets are generics: they tell the compiler what the list holds, so mistakes are caught at compile time instead of at runtime.

import java.util.*;

List<String> names = new ArrayList<>();
names.add("Ada");
names.add("Linus");
names.add("Grace");

System.out.println(names.get(1));
System.out.println(names.size());
names.remove("Linus");
System.out.println(names);
Output
Linus
3
[Ada, Grace]

Set and HashSet

A HashSet silently ignores duplicates and answers "is this in here?" in roughly constant time. Use LinkedHashSet to keep insertion order or TreeSet for sorted order.

Set<Integer> seen = new HashSet<>();
System.out.println(seen.add(5));   // true
System.out.println(seen.add(5));   // false, already present
System.out.println(seen.contains(5));
Output
true
false
true

Map and HashMap

A Map stores values by key. getOrDefault and merge make counting easy without null checks.

Map<String, Integer> counts = new HashMap<>();
for (String w : "to be or not to be".split(" ")) {
    counts.merge(w, 1, Integer::sum);
}
System.out.println(counts.get("to"));

for (Map.Entry<String, Integer> e : counts.entrySet()) {
    System.out.println(e.getKey() + "=" + e.getValue());
}
Output
2
(entries in hash order, for example: not=1, be=2, or=1, to=2)

Choosing the right one

  • Need order and index access? ArrayList.
  • Need fast add/remove at both ends? ArrayDeque.
  • Need uniqueness or fast contains? HashSet.
  • Need lookup by key? HashMap.
  • Need keys kept sorted? TreeMap / TreeSet.

Sorting and streams

List<Integer> nums = new ArrayList<>(List.of(5, 2, 9, 1));
Collections.sort(nums);
System.out.println(nums);

List<Integer> big = nums.stream().filter(n -> n > 2).toList();
System.out.println(big);
Output
[1, 2, 5, 9]
[5, 9]

List: an ordered, growable sequence

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> tasks = new ArrayList<>();
        tasks.add("write");
        tasks.add("test");
        tasks.add("ship");
        tasks.remove("test");

        System.out.println(tasks);
        System.out.println(tasks.size() + " tasks, first = " + tasks.get(0));
        System.out.println(tasks.contains("ship"));
    }
}
Output
[write, ship]
2 tasks, first = write
true

The <String> is a generic: it tells the compiler this list only holds strings, so adding a number is a compile error.

Set: uniqueness for free

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Set<String> tags = new TreeSet<>();
        tags.add("java");
        tags.add("sql");
        tags.add("java");        // duplicate ignored
        System.out.println(tags);
        System.out.println(tags.size());
    }
}
Output
[java, sql]
2

Map: look things up by key

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> stock = new TreeMap<>();
        stock.put("apple", 5);
        stock.put("pear", 2);
        stock.merge("apple", 3, Integer::sum);   // add to existing

        for (Map.Entry<String, Integer> e : stock.entrySet()) {
            System.out.println(e.getKey() + " -> " + e.getValue());
        }
        System.out.println(stock.getOrDefault("kiwi", 0));
    }
}
Output
apple -> 8
pear -> 2
0

Worked example: count word frequency

import java.util.*;

public class Main {
    public static void main(String[] args) {
        String text = "the cat and the hat and the bat";
        Map<String, Integer> counts = new TreeMap<>();
        for (String w : text.split(" ")) {
            counts.merge(w, 1, Integer::sum);
        }
        System.out.println(counts);
    }
}
Output
{and=2, bat=1, cat=1, hat=1, the=3}

Key takeaways

  • List keeps order and allows duplicates; Set rejects duplicates; Map stores key-value pairs.
  • Generics like List<String> make the compiler check element types.
  • Program to the interface (List) and choose the implementation (ArrayList) on the right.
  • Map.merge is a neat way to count things.
// Write your solution here
Up next · Lesson 6Exception Handlingtry/catch/finally and throwing checked exceptions.