Chapter 4 of 18
04 — Lists
List.of, enhanced for-loops, and your first streams refactor.
Kata: Sum.sum(List<Integer>) and Sum.sumAll(List<List<Integer>>) — summing one list,
then a list of lists.
New ideas: List, List.of, the enhanced for-each loop, and your first streams refactor.
Step 1 — write the test for sum
@Test
void sumsANonEmptyList() {
assertEquals(15, Sum.sum(List.of(1, 2, 3, 4, 5)));
}
Run it: RED (Sum missing). Then the minimum:
public static int sum(List<Integer> numbers) {
int total = 0;
for (int number : numbers) {
total += number;
}
return total;
}
GREEN.
Step 2 — List.of is your test fixture literal
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
Note: List.of() null-forbids and produces an immutable list. You can’t append. That’s a
feature — it teaches you to reach for streams and toList() rather than mutating.
Step 3 — sumAll: compose the function you already have
A function summing a list of lists:
public static int sumAll(List<List<Integer>> numbersLists) {
int total = 0;
for (List<Integer> numbers : numbersLists) {
total += sum(numbers);
}
return total;
}
Drive it with tests. List.of(List.of(1, 2), List.of(0, 9), List.of(3)) → 15. The enhanced
for-each loop composes — calling the sum you already have.
REFACTOR — the streams kick-off
Now the big refactor. Your loop can collapse into a pipeline:
public static int sum(List<Integer> numbers) {
return numbers.stream().mapToInt(Integer::intValue).sum();
}
Same test, same answer, expression-based instead of statement-based. State (total), mutation,
and the loop disappear — the compiler reasons about it for you.
Don’t refactor this away yet if it feels abstract. Chapter 08 makes you fluent in streams. What matters now: when you see a collection, the first thought for “reduce to one value” should become a stream pipeline.
Java-specific notes
int total = 0isIntegerboxed/unboxed automatically —List<Integer>stores objects.mapToInt(...)avoids boxing; it turns the stream into a primitiveIntStream.List.of()(Java 9+) is the standard fixture literal. Prefer it overArrays.asListor hand-rollednew ArrayList<>(...)in tests.
Run it
gradle test --tests "collections.SumTest"
Key takeaway: generics give you a List<Integer> whose element type is proven at compile
time. And the collection → stream refactor is a habit modern Java codebases expect you to have.
Next: 05 — structs — records and geometry, where TDD meets data types.