Collections and generics
Java's Collections Framework provides ready-made data structures. Generics let those structures hold any type while keeping full compile-time type checking.
Arrays and their limits
A Java array has a fixed length set at creation: int[] marks = new int[5];. Arrays are fast, but they cannot grow, and inserting in the middle means shifting elements by hand. The Collections Framework, in the java.util package, provides flexible alternatives.
Lists
The List interface represents an ordered sequence. ArrayList, backed by a resizable array, offers fast access by index; LinkedList makes insertion at the ends cheap. Typical use: List<String> names = new ArrayList<>(); names.add("Ana"); names.get(0);. Programming to the interface type, List, lets you swap implementations later.
Sets and maps
A Set holds unique elements: HashSet is fast and unordered, TreeSet keeps elements sorted, and LinkedHashSet remembers insertion order. A Map stores key-value pairs: Map<String, Integer> ages = new HashMap<>(); ages.put("Ana", 15);. getOrDefault avoids null checks, and TreeMap keeps keys sorted.
Generics
Generics add type parameters: List<String> can hold only strings, so the compiler rejects names.add(42) and no casts are needed when reading. You can write your own generic classes, such as class Box<T> { private T item; }, and generic methods, such as static <T> T first(List<T> list).
Primitives and wrappers
Collections hold objects, not primitives, so Java provides wrapper classes: Integer for int, Double for double, and so on. Autoboxing converts automatically: list.add(5) stores an Integer. Be careful comparing wrappers with ==, which compares references; use equals or unbox to int first.
Iterating and sorting
The enhanced for loop iterates any collection: for (String n : names). Removing elements while looping that way throws ConcurrentModificationException; use an Iterator's remove method or removeIf instead. Collections.sort(list) or list.sort(...) sorts; a Comparator defines custom orders, such as Comparator.comparing(Student::getGrade).
Lambdas and streams
Java 8 added lambda expressions, short anonymous functions such as x -> x * 2, and the Stream API for processing collections declaratively: names.stream().filter(n -> n.startsWith("A")).map(String::toUpperCase).toList(). Streams chain operations such as filter, map, and reduce, often replacing explicit loops.
Worked example
Counting word frequencies: Map<String, Integer> counts = new HashMap<>(); for (String w : text.split(" ")) { counts.merge(w, 1, Integer::sum); }. For "to be or not to be", counts become to=2, be=2, or=1, not=1. With streams: Arrays.stream(text.split(" ")).collect(Collectors.groupingBy(w -> w, Collectors.counting())).
Key takeaways
Arrays have fixed length; the Collections Framework in java.util offers flexible structures. ArrayList and LinkedList implement List; program to interfaces. HashSet, TreeSet, HashMap, and TreeMap provide sets and maps. Generics give type-safe collections without casts. Wrapper classes and autoboxing let collections hold numbers; compare wrappers with equals. Use removeIf or iterators to remove while iterating; Comparators define orders. Lambdas and streams process collections declaratively.
Flashcard
Resizable array-based list
Check your understanding
Why declare List<String> names = new ArrayList<>(); rather than ArrayList<String> names?
