Collections Framework I

Course 1 · Ch 8
Collections Framework I
Java's own answer to the STL containers — and why boxing from Chapter 2 was never optional here

Fundamentals closes with the tool used constantly in real Java code: growable, generic collections — starting with List, the interface Course 2's own Collections Framework II picks up from with Map/Set/Queue.

ArrayList — A Growable Array

int[] fixedArray = new int[5]; // size is locked in forever at creation List<String> names = new ArrayList<>(); names.add("Alice"); names.add("Bob"); // grows automatically — no size decided up front

A plain Java array's size is fixed the moment it's created. ArrayList is backed internally by an array too, but resizes itself automatically as elements are added — the size a caller actually deals with is never fixed at all.

Generics in Collections

List<String> tells the compiler exactly what type this list holds, catching a type mismatch at compile time rather than at some later runtime cast. Before generics existed, Java collections held plain Object references, requiring an explicit, unchecked cast on every read — a real source of runtime ClassCastExceptions generics eliminate outright. Course 2's own Generics In Depth chapter goes further into how generics work themselves; this chapter only needs the collection-facing side.

Programming to the Interface

List<String> names = new ArrayList<>(); // declared type: List — the interface, not the implementation

The variable is declared as List<String>, not ArrayList<String>, even though an ArrayList is what's actually constructed. This mirrors java1-6's own interface-vs-implementation split directly: code written against the List interface keeps working unchanged if the concrete implementation is later swapped for a LinkedList or any other List.

Iterating — for-each, Iterator, and Safe Removal

for (String name : names) { // the for-each form — simplest, most common System.out.println(name); } Iterator<String> it = names.iterator(); while (it.hasNext()) { String name = it.next(); if (name.equals("Bob")) { it.remove(); // safe removal DURING iteration — only via the Iterator itself } }

The for-each form is the everyday choice for read-only iteration. Removing an element while iterating requires the explicit Iterator and its own remove() — removing directly from the list during a for-each is not safe, covered next.

Java's Own Answer to the STL Containers

ArrayList is directly comparable to cpp2-2's own std::vector — both are growable, contiguous, general-purpose sequence containers. The real difference traces straight back to java1-2's boxing material: a C++ std::vector<int> stores raw int values directly, no wrapping required, since C++ templates generate real code specialized for the exact type. Java generics can't hold a primitive int at all — List<int> doesn't compile — so List<Integer> is required instead, and every element is autoboxed on the way in.

BehaviorC++ std::vector (cpp2-2)Java ArrayList
Growableyesyes
Holding a primitive/raw intyes — stored directly, no wrappingno — must box to Integer (java1-2)
Declared-type conventionthe concrete container type itselfthe interface (List), not ArrayList
Declare with the interface, construct with the implementation
List<String> list = new ArrayList<>(); — this single habit keeps calling code decoupled from exactly which List implementation is in use, the same interface-over-implementation instinct from java1-6.
Removing during a for-each throws at runtime
Calling list.remove(...) directly on the list while a for-each loop is iterating over it throws ConcurrentModificationException — the for-each's hidden iterator detects the list changed underneath it. Use Iterator.remove() instead, the only safe way to remove elements mid-iteration.

Coding Challenges

Challenge 1

Create a List<Integer> declared using the List interface, add five numbers to it via an ArrayList, then print them using a for-each loop and explain in a comment where autoboxing happens.

📄 View solution
Challenge 2

Write code that removes every element equal to a target value from a List<String> directly during a for-each loop, showing the resulting ConcurrentModificationException, then fix it correctly using an explicit Iterator's remove() method.

📄 View solution
Challenge 3

Explain why List<int> does not compile in Java, but List<Integer> does, tying your answer back to java1-2's own primitives-vs-reference-types material and to how C++'s std::vector<int> differs.

📄 View solution

Chapter 8 Quick Reference — Course 1 Complete

  • ArrayList resizes automatically, unlike a plain Java array's fixed size
  • Generics catch type mismatches at compile time, replacing the pre-generics Object-cast pattern
  • Declare with the interface (List), construct with the implementation (ArrayList) — mirrors java1-6's interface-vs-implementation split
  • Removing during a for-each throws ConcurrentModificationException — use Iterator.remove() instead
  • ArrayList is Java's answer to cpp2-2's std::vector, but genuinely can't hold primitives directly — everything boxes, per java1-2
  • Java Fundamentals is now complete. Course 2 (Intermediate/Advanced) begins with Generics In Depth — Java's type-erasure strategy, contrasted directly against C++ and Rust's shared monomorphization approach.