Arrays & Collections

Arrays

Fixed length, zero-based:

int[] numbers = new int[5];
String[] colors = {"red", "green", "blue"};

colors.length     // 3 — a field, not a method
colors[1]         // "green"
colors[3]         // ArrayIndexOutOfBoundsException

Arrays.sort(numbers);
Arrays.toString(numbers);

Arrays are rigid. In practice you use a List.

List

Ordered, resizable, duplicates allowed:

var names = new ArrayList<String>();
names.add("Ann");
names.add("Ben");

names.get(0)            // "Ann"
names.size()            // 2
names.contains("Ben")   // true
names.indexOf("Ben")    // 1
names.remove("Ben");
names.set(0, "Anne");
names.isEmpty()

List<String> means “list of strings” — the compiler keeps everything else out. On the right side the diamond <> is enough: List<String> names = new ArrayList<>();

var days = List.of("Mon", "Tue", "Wed");   // immutable
days.add("Thu");                            // UnsupportedOperationException

var copy = new ArrayList<>(days);           // mutable copy
Tip

Default to List.of(...). Immutable is the common case and prevents accidental changes.

for (var name : names) IO.println(name);

for (var i = 0; i < names.size(); i++) IO.println(i + ": " + names.get(i));

names.forEach(IO::println);

Set

No duplicates. HashSet (unordered), LinkedHashSet (insertion order), TreeSet (sorted).

var tags = new HashSet<String>();
tags.add("java");
tags.add("java");     // no effect
tags.size()           // 1

var a = Set.of(1, 2, 3, 4);
var b = Set.of(3, 4, 5);

var intersection = new HashSet<>(a); intersection.retainAll(b);   // [3, 4]
var union        = new HashSet<>(a); union.addAll(b);             // [1..5]
var difference   = new HashSet<>(a); difference.removeAll(b);     // [1, 2]

Map

Keys → values, keys are unique.

var ages = new HashMap<String, Integer>();
ages.put("Ann", 30);
ages.put("Ann", 31);            // overwrites

ages.get("Ann")                 // 31
ages.get("Zoe")                 // null
ages.getOrDefault("Zoe", 0)     // 0
ages.containsKey("Ann")
ages.remove("Ann");

Useful patterns:

counts.merge("java", 1, Integer::sum);              // count things
counts.putIfAbsent("course", 1);
groups.computeIfAbsent("a", k -> new ArrayList<>()).add("Ann");

for (var e : ages.entrySet()) IO.println(e.getKey() + " " + e.getValue());
ages.forEach((name, age) -> IO.println(name + ": " + age));

var capitals = Map.of("Germany", "Berlin", "France", "Paris");   // immutable

Which one?

Need Type
ordered, duplicates ok ArrayList
no duplicates, fast lookup HashSet
no duplicates, sorted TreeSet
key → value HashMap
key → value, sorted TreeMap
queue / stack ArrayDeque

Records: bundling data

When values belong together, use a type — not a map:

record Person(String name, int age) {}

var ann = new Person("Ann", 30);
ann.name()      // "Ann"
IO.println(ann) // Person[name=Ann, age=30]

You get the constructor, accessors, equals, hashCode and toString for free — which also makes records good map keys:

record Coordinate(int x, int y) {}

var map = new HashMap<Coordinate, String>();
map.put(new Coordinate(1, 2), "treasure");
map.get(new Coordinate(1, 2));   // "treasure"  different object, equal content

More on records in chapter 11.

★ Exercises

  1. Sum, average, min and max of ten numbers, using a loop.
  2. Remove duplicates from a list — once with a Set, once by hand.
  3. Count letter frequency of a sentence in a Map<Character, Integer>.
  4. Build a phone book Map<String, String> with add, lookup and delete methods.
  5. Define record Item(String name, double price, int qty) and total a cart of five items.
  6. Turn a List<String> into a Map<Integer, List<String>> grouped by word length.