CS2030S PE Syntax Cheatsheet

Lego-style reference. Each block is a plug-and-play pattern with a one-liner on when to use it.


1. Class Basics

Minimal class

class Point {
    private final double x;
    private final double y;
 
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

When: whenever you need a new reference type.

Static (class) field / method

class Circle {
    public static final double PI = 3.14159;   // constant
    private static int lastId = 0;              // class state
 
    public static int getCount() {               // class method
        return lastId;
    }
}

When: value or behaviour tied to the class, not individual instances. public static final = constant. this forbidden inside static methods.

main entry point

public static void main(String[] args) { ... }

this inside constructor — call another constructor

class Circle {
    Circle(Point c, double r) { this.c = c; this.r = r; }
    Circle() { this(new Point(0, 0), 1.0); }   // delegates
}

2. Inheritance

extends with super

class ColoredCircle extends Circle {
    private final Color color;
 
    public ColoredCircle(Point c, double r, Color color) {
        super(c, r);          // call parent constructor first
        this.color = color;
    }
}

When: child IS-A parent (substitutable). Use composition instead for HAS-A.

@Override a method

@Override
public String toString() {
    return super.toString() + " color=" + this.color;
}

When: redefining a parent/interface method. Annotation makes the compiler catch typos.

Prevent inheritance / overriding / reassignment

final class Circle { ... }                // cannot be extended
public final int getArea() { ... }        // cannot be overridden
private final double r;                   // cannot be reassigned

3. Abstract Class

abstract class Shape {
    private int sides;                       // may have fields
    public boolean hasSides() { return sides > 0; }   // may have concrete methods
    public abstract double getArea();        // no body — subclass must override
}
 
class Circle extends Shape {
    private double r;
    @Override
    public double getArea() { return Math.PI * r * r; }
}

When: want to force subclasses to implement a method but share some state/behaviour. Cannot new Shape().


4. Interface

Declaring and implementing

interface Shape {
    double getArea();                       // implicitly public abstract
}
 
interface Movable {
    Movable moveBy(double dx, double dy);
}
 
class Circle implements Shape, Movable {    // can implement many
    @Override public double getArea() { ... }
    @Override public Circle moveBy(double dx, double dy) { ... }
}

When: CAN-DO capability shared across unrelated class hierarchies. Name usually ends in -able.

Interface extending interface

interface MovableShape extends Shape, Movable { }   // no new methods needed

Functional interface

@FunctionalInterface
interface Transformer<T, R> {
    R transform(T t);                       // exactly one abstract method
}

When: you want a lambda-compatible interface. Enables lambda syntax.


5. Generics

Generic class

class Pair<S, T> {
    private final S first;
    private final T second;
 
    public Pair(S first, T second) {
        this.first = first;
        this.second = second;
    }
 
    public S getFirst()  { return this.first; }
    public T getSecond() { return this.second; }
}
 
Pair<String, Integer> p = new Pair<>("hi", 4);   // diamond <> infers types

When: class holds elements of a type you want to lock in at instantiation.

Generic method

class A {
    public static <T> boolean contains(T[] arr, T obj) {
        for (T x : arr) if (x.equals(obj)) return true;
        return false;
    }
}
 
A.<String>contains(strArr, "hi");   // explicit type witness
A.contains(strArr, "hi");           // inferred

Bounded type parameter

public static <T extends Shape> T findLargest(T[] arr) { ... }

When: T needs methods from some supertype.

Self-referential bound (for Comparable)

class Pair<S extends Comparable<S>, T> implements Comparable<Pair<S, T>> { ... }

Bypassing array restriction inside generics

class Seq<T> {
    private final T[] array;
    public Seq(int size) {
        @SuppressWarnings("unchecked")
        T[] a = (T[]) new Object[size];     // Java forbids new T[size]
        this.array = a;
    }
}

Always comment why the suppression is safe.


6. Wildcards (PECS) **

? extends T — producer (read-only)

public void copyFrom(Seq<? extends T> src) {    // reading OUT of src
    for (int i = 0; i < n; i++) this.set(i, src.get(i));
}

? super T — consumer (write-only)

public void copyTo(Seq<? super T> dest) {       // writing INTO dest
    for (int i = 0; i < n; i++) dest.set(i, this.get(i));
}

? — unbounded (read as Object only, can only write null)

void printAny(Seq<?> seq) { System.out.println(seq.get(0)); }

The PECS rule

Producer Extends, Consumer Super. If the param produces T for you → extends. If you feed T into the param → super. Exact type Seq<T> if you do both.

Variance summary

FormSubtype relation
Seq<Integer> vs Seq<Number>invariant (no relation)
Seq<Integer> <: Seq<? extends Number>covariant
Seq<Number> <: Seq<? super Integer>contravariant
Java arrays Integer[] <: Number[]covariant (dangerous)

7. Casting & instanceof

Shape s = new Circle(...);
 
// Pattern-matched instanceof (Java 16+)
if (s instanceof Circle c) {            // auto-casts to c
    c.getRadius();
}
 
// Traditional cast
Circle c = (Circle) s;                   // throws ClassCastException if wrong

8. Enums & Wrapper Types

Wrapper boxing

Integer i = 4;       // autoboxed
int j = i;           // auto-unboxed

Enum

enum LogLevel { INFO, WARNING, ERROR }

9. Exceptions **

try / catch / finally

try {
    FileReader fr = new FileReader("a.txt");
    int x = new Scanner(fr).nextInt();
} catch (FileNotFoundException e) {
    System.err.println("missing: " + e);
} catch (InputMismatchException | NoSuchElementException e) {  // multi-catch
    System.err.println(e);
} finally {
    // always runs
}

Throw / throws

public Circle(Point c, double r) throws IllegalArgumentException {
    if (r < 0) throw new IllegalArgumentException("negative radius");
    ...
}

Custom exception

class IllegalCircleException extends IllegalArgumentException {
    IllegalCircleException(String msg) { super(msg); }
}

Checked vs unchecked

  • Checked (extends Exception, not RuntimeException): must catch or declare throws. Example: IOException, FileNotFoundException.
  • Unchecked (extends RuntimeException): optional. Example: NullPointerException, IllegalArgumentException.

When overriding, the override can throw fewer or more specific checked exceptions, never broader.


10. Nested / Local / Anonymous Classes

Static nested class

class A {
    private static int y;
    private static class C { void bar() { y = 1; } }   // only sees static
}

Inner (non-static) class

class A {
    private int x;
    private class B { void foo() { A.this.x = 1; } }   // qualified `this`
}

Local class (inside a method)

void sort(List<String> names) {
    class ByLength implements Comparator<String> {
        public int compare(String a, String b) { return a.length() - b.length(); }
    }
    names.sort(new ByLength());
}

Anonymous class

names.sort(new Comparator<String>() {
    @Override public int compare(String a, String b) { return a.length() - b.length(); }
});

Captured locals must be final or effectively final.


11. Lambdas & Method References

Lambda forms

x -> x * x                              // one param, expression body
(x, y) -> x + y                         // two params
x -> { return x + 1; }                  // block body
() -> "hello"                           // no params
(Integer x) -> x + 1                    // typed param (rare)

Assigning to functional interface

Transformer<Integer, Integer> sq = x -> x * x;
sq.transform(5);                        // 25

Method references

Box::of              // x -> Box.of(x)           static method
Box::new             // x -> new Box(x)          constructor
origin::distanceTo   // y -> origin.distanceTo(y)  bound instance
Integer::compareTo   // (x, y) -> x.compareTo(y)   unbound instance

Currying (multi-arg as nested lambdas)

Transformer<Integer, Transformer<Integer, Integer>> add = x -> y -> x + y;
add.transform(1).transform(2);          // 3
Transformer<Integer, Integer> incr = add.transform(1);  // partial application

12. Java’s Functional Interfaces (java.util.function)

InterfaceMethodShape
Function<T, R>apply(T)T → R
UnaryOperator<T>apply(T)T → T
BiFunction<T, U, R>apply(T, U)(T, U) → R
Predicate<T>test(T)T → boolean
Supplier<T>get()() → T
Consumer<T>accept(T)T → void
Runnablerun()() → void
Comparator<T>compare(T, T)(T, T) → int

CS2030S custom mappings: Transformer = Function, Producer = Supplier, BooleanCondition = Predicate, Combiner = BiFunction.


13. Optional / Maybe

Creating

Optional<String> a = Optional.of("hi");           // value must not be null
Optional<String> b = Optional.empty();             // no value
Optional<String> c = Optional.ofNullable(maybe);   // null-safe

Chaining

optional
    .filter(s -> s.length() > 0)          // Optional<T>; drops value if fails predicate
    .map(s -> s.length())                 // Optional<R>
    .flatMap(n -> lookup(n))              // Optional<R> (lookup returns Optional<R>)
    .ifPresent(System.out::println)       // runs consumer if present
    .orElse(defaultValue)                  // unwrap with default
    .orElseGet(() -> computeDefault())    // unwrap with lazy default
    .orElseThrow(() -> new RuntimeException());

When: replace null returns. map transforms, flatMap avoids Optional<Optional<R>>.

Custom Maybe skeleton

class Maybe<T> {
    private final T value;    // null = empty
    private Maybe(T v) { this.value = v; }
 
    public static <T> Maybe<T> of(T v)      { return new Maybe<>(v); }
    public static <T> Maybe<T> empty()      { return new Maybe<>(null); }
 
    public <R> Maybe<R> map(Function<? super T, ? extends R> f) {
        if (value == null) return empty();
        return Maybe.of(f.apply(value));
    }
 
    public <R> Maybe<R> flatMap(Function<? super T, ? extends Maybe<? extends R>> f) {
        if (value == null) return empty();
        @SuppressWarnings("unchecked")
        Maybe<R> result = (Maybe<R>) f.apply(value);
        return result;
    }
 
    public T orElse(T def) { return value == null ? def : value; }
}

14. Lazy

Custom Lazy class

class Lazy<T> {
    private T value;
    private boolean evaluated = false;
    private Supplier<? extends T> producer;
 
    public Lazy(Supplier<? extends T> p) { this.producer = p; }
 
    public T get() {
        if (!evaluated) {
            value = producer.get();
            evaluated = true;
        }
        return value;
    }
 
    public <R> Lazy<R> map(Function<? super T, ? extends R> f) {
        return new Lazy<>(() -> f.apply(this.get()));
    }
 
    public <R> Lazy<R> flatMap(Function<? super T, ? extends Lazy<? extends R>> f) {
        return new Lazy<>(() -> f.apply(this.get()).get());
    }
}
 
Lazy<String> msg = new Lazy<>(() -> "User: " + System.getProperty("user.name"));
msg.get();   // computed now, cached from here on

When: expensive computation you want to run at most once, only when needed.


15. InfiniteList (custom)

class InfiniteList<T> {
    private final Supplier<T> head;
    private final Supplier<InfiniteList<T>> tail;
 
    public InfiniteList(Supplier<T> h, Supplier<InfiniteList<T>> t) {
        this.head = h; this.tail = t;
    }
 
    public T head() { return this.head.get(); }
    public InfiniteList<T> tail() { return this.tail.get(); }
 
    public static <T> InfiniteList<T> generate(Supplier<T> s) {
        return new InfiniteList<>(s, () -> generate(s));
    }
 
    public static <T> InfiniteList<T> iterate(T init, UnaryOperator<T> next) {
        return new InfiniteList<>(() -> init,
                () -> iterate(next.apply(init), next));
    }
 
    public <R> InfiniteList<R> map(Function<? super T, ? extends R> f) {
        return new InfiniteList<>(
                () -> f.apply(this.head()),
                () -> this.tail().map(f));
    }
}

When: demonstrate lazy lists. Always wrap both head and tail in producers.


16. Stream API (java.util.stream)

Build a stream

Stream.of(1, 2, 3)
Stream.generate(() -> 1)                          // infinite
Stream.iterate(0, x -> x + 1)                     // infinite
Stream.iterate(0, x -> x < 100, x -> x + 1)       // finite, Java 9+
Arrays.stream(arr)
list.stream()
IntStream.range(0, 10)                            // 0..9
IntStream.rangeClosed(0, 10)                      // 0..10

Intermediate (lazy, return Stream)

.filter(x -> x > 0)
.map(x -> x * 2)
.mapToInt(Integer::intValue)                      // Stream<Integer> → IntStream
.mapToObj(i -> "n=" + i)                          // IntStream → Stream<String>
.flatMap(s -> s.chars().boxed())                  // flatten nested streams
.distinct()                                        // stateful, bounded
.sorted()  /  .sorted(comparator)                 // stateful, bounded
.limit(n)                                          // truncate
.skip(n)
.takeWhile(x -> x < 10)                           // Java 9+
.dropWhile(x -> x < 10)                           // Java 9+
.peek(System.out::println)                        // side channel for debug
.boxed()                                          // IntStream → Stream<Integer>
.parallel()  /  .sequential()
.unordered()

Terminal (eager, triggers pipeline)

.forEach(System.out::println)
.forEachOrdered(System.out::println)
.toList()                                          // Java 16+
.toArray()
.count()
.reduce(0, (a, b) -> a + b)                       // with identity, returns T
.reduce((a, b) -> a + b)                          // no identity, returns Optional<T>
.reduce(0, (u, t) -> ..., (u1, u2) -> ...)        // 3-arg: identity, accumulator, combiner
.sum()  /  .min()  /  .max()  /  .average()       // on primitive streams
.findFirst()  /  .findAny()                       // return Optional<T>
.anyMatch(pred)  /  .allMatch(pred)  /  .noneMatch(pred)
.collect(Collectors.toList())
.collect(Collectors.toSet())
.collect(Collectors.joining(", "))
.collect(Collectors.groupingBy(keyFn))

Canonical prime example

boolean isPrime(int n) {
    return n > 1 && IntStream.range(2, (int) Math.sqrt(n) + 1)
            .noneMatch(x -> n % x == 0);
}
 
IntStream.iterate(2, x -> x + 1)
    .filter(x -> isPrime(x))
    .limit(500)
    .forEach(System.out::println);

Important gotchas

  • A stream can only be consumed once. Recreate for each pipeline.
  • sorted and distinct need a finite stream — never on an infinite source without limit first.
  • parallel() is a marker — place anywhere before terminal.

17. Comparator

Comparator<String> byLen  = (a, b) -> a.length() - b.length();
Comparator<String> byLen2 = Comparator.comparingInt(String::length);
Comparator<Person> byAge  = Comparator.comparing(Person::getAge);
 
list.sort(byLen);
list.sort(byAge.reversed().thenComparing(Person::getName));
 
Comparator.naturalOrder();
Comparator.reverseOrder();

Comparable (natural ordering on the class itself)

class Person implements Comparable<Person> {
    @Override public int compareTo(Person other) {
        return this.age - other.age;
    }
}

18. Parallel Stream

IntStream.range(2_000_000, 3_000_000)
    .parallel()
    .filter(x -> isPrime(x))
    .count();

Parallelisation rules

  • No interference: don’t modify the source while streaming.
  • Stateless lambdas: avoid captured mutable state.
  • No side effects: use collect/toList instead of forEach(list::add).
  • Associative accumulator & combiner for reduce: (a op b) op c == a op (b op c).
  • reduce(identity, acc, combiner) rules: combiner(identity, i) == i, combiner(u, acc(identity, t)) == acc(u, t).

Creating parallel collection stream

list.parallelStream().filter(...).toList();

19. Threads

Creating a thread with Runnable

Thread t = new Thread(() -> {
    for (int i = 0; i < 10; i++) System.out.print("_");
});
t.start();           // non-blocking; runs the runnable in a new thread
t.isAlive();         // still running?
t.join();            // block until t finishes

Thread introspection

Thread.currentThread().getName();
Thread.sleep(1000);                      // throws InterruptedException (checked)

Wrap in try/catch

try { Thread.sleep(1000); } catch (InterruptedException e) { }

20. CompletableFuture (async)

Creating

CompletableFuture<Integer> a = CompletableFuture.completedFuture(42);
CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> slowPrime());
CompletableFuture<Void>    c = CompletableFuture.runAsync(() -> System.out.println("hi"));

Chaining

cf.thenApply(x -> x + 1);                   // map
cf.thenCompose(x -> nextCF(x));             // flatMap
cf.thenCombine(other, (x, y) -> x + y);     // combine two
cf.thenRun(() -> System.out.println("done"));
 
// Async versions run lambda in a different thread
cf.thenApplyAsync(...);
cf.thenComposeAsync(...);
cf.thenCombineAsync(...);

Multi-future combinators

CompletableFuture.allOf(cf1, cf2, cf3);     // completes when ALL done
CompletableFuture.anyOf(cf1, cf2, cf3);     // completes when ANY done

Getting the value

cf.get();       // throws InterruptedException + ExecutionException (checked)
cf.join();      // throws unchecked CompletionException

Exception handling

cf.handle((value, ex) -> ex == null ? value : -1);   // BiFunction(value, throwable)
cf.exceptionally(ex -> fallbackValue);
cf.whenComplete((v, ex) -> log(v, ex));

21. ForkJoin (RecursiveTask)

Skeleton

class Summer extends RecursiveTask<Integer> {
    private static final int THRESHOLD = 2;
    private final int low, high;
    private final int[] arr;
 
    Summer(int low, int high, int[] arr) {
        this.low = low; this.high = high; this.arr = arr;
    }
 
    @Override
    protected Integer compute() {
        if (high - low < THRESHOLD) {          // base case — sequential
            int s = 0;
            for (int i = low; i < high; i++) s += arr[i];
            return s;
        }
        int mid = (low + high) / 2;
        Summer left  = new Summer(low, mid, arr);
        Summer right = new Summer(mid, high, arr);
 
        left.fork();                           // >---------+
        return right.compute() + left.join();  // <---------+  palindrome order
    }
}
 
// invoke
Summer task = new Summer(0, arr.length, arr);
int total = task.compute();

Rules

  • Palindrome pattern: fork() ... fork() ... compute() ... join() ... join() — reverse order of fork/join, compute() in the middle, no crossings.
  • Never left.compute() + right.compute() — that’s sequential.

22. Immutability Skeleton

final class Point {                          // final class — no subclassing
    private final double x;                   // final fields
    private final double y;
 
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
 
    public Point moveTo(double nx, double ny) {
        return new Point(nx, ny);             // return new instance, don't mutate
    }
}

Recipe: final class + all fields private final + methods return new instances + no setters.


23. Variadic & Annotations

Varargs

@SafeVarargs
public static <T> Seq<T> of(T... items) {     // items is T[]
    ...
}
Seq.of(1, 2, 3);

Useful annotations

AnnotationUse
@OverrideMethod is meant to override a parent method
@FunctionalInterfaceInterface is meant to have exactly one abstract method
@SuppressWarnings("unchecked")Suppress unchecked cast warning (only when provably safe)
@SafeVarargsGeneric varargs usage is safe

24. Monad/Functor Laws (theoretical, for MCQ)

Functor laws (anything with map):

  • Identity: m.map(x -> x)m
  • Composition: m.map(f).map(g)m.map(x -> g(f(x)))

Monad laws (anything with flatMap + of):

  • Left identity: M.of(x).flatMap(f)f(x)
  • Right identity: m.flatMap(x -> M.of(x))m
  • Associativity: m.flatMap(f).flatMap(g)m.flatMap(x -> f(x).flatMap(g))

25. Quick Reference — subtype notation

NotationMeaning
S <: TS is a subtype of T
Circle <: ShapeCircle is-a Shape
S <: T and C(S) <: C(T)C is covariant in its type parameter
S <: T and C(T) <: C(S)C is contravariant
C(S) and C(T) unrelatedC is invariant (Java generics default)

26. Common gotchas to remember

  1. Generics are invariant: List<Integer> is NOT a List<Number>. Use wildcards.
  2. Arrays are covariant: Integer[] IS an Object[]. Can cause ArrayStoreException.
  3. Type erasure: cannot do new T[], new Pair<String,Integer>[], obj instanceof Pair<String> — but Pair<?> works.
  4. Streams consume once: recreate for each pipeline.
  5. Captured variables must be effectively final in lambdas / local / anonymous classes.
  6. Autoboxing in streams is costly: prefer IntStream over Stream<Integer> for numeric work.
  7. equals must take Object to properly override — not Circle. Use instanceof inside.
  8. Overriding return type can be narrower (covariant return), parameters cannot change.
  9. Overloading = same name, different signature. Overriding = same descriptor (signature + return type).
  10. Parallel stream reduce: identity must be neutral, accumulator + combiner must be associative and compatible.

String.format(“%.2f”, this.x)

Q1 — Immutable class

java

final class Vector {
    private final double x;
    private final double y;
 
    Vector(double x, double y) {
        this.x = x;
        this.y = y;
    }
 
    Vector add(Vector other) {
        return new Vector(this.x + other.x, this.y + other.y);
    }
 
    @Override
    public String toString() {
        return String.format("<%.2f, %.2f>", this.x, this.y);
    }
}

Q2 — Comparable (natural ordering)

java

class Student implements Comparable<Student> {
    private final String name;
    private final int score;
 
    Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
 
    @Override
    public int compareTo(Student other) {
        return this.score - other.score; // if negative, then it goes first
    }
}

Q3 — Comparator (external ordering), two forms

java

// 1. Named class form
class ByName implements Comparator<Student> {
    @Override
    public int compare(Student a, Student b) {
        return a.getName().compareTo(b.getName());
    }
}
 
// 2. Lambda form
Comparator<Student> byName = (a, b) -> a.getName().compareTo(b.getName());

Q4 — Comparator with tie-breaker (lambda)

java

 // Ternary one-liner (terser, same behavior)
Comparator<Student> byScoreDescThenName2 = (a, b) ->
    b.getScore() != a.getScore()
        ? b.score - a.score
        : a.getName().compareTo(b.getName()); // natural ordering for text

Q5 — Comparator.comparing pipeline

java

Comparator<Student> byScoreDescThenName =
    Comparator.comparingInt(Student::getScore)
              .reversed()
              .thenComparing(Student::getName);

Q6 — Stream: filter + sort + map + collect

java

List<String> result = students.stream()
    .filter(s -> s.getScore() >= 50)
    .sorted(Comparator.comparingInt(Student::getScore).reversed())
    .map(Student::getName)
    .toList();

Q7 — Stream reduce: sum (two ways)

java

// Version 1: mapToInt + sum (preferred)
int total = students.stream()
    .mapToInt(Student::getScore)
    .sum();
 
// Version 2: map + reduce with identity
int total = students.stream()
    .mapToInt(Student::getScore)
    .reduce(0, Integer::sum);

Q8 — Optional chaining with map / filter / orElse

java

String grade = findStudent("A123")
    .map(Student::getScore)
    .filter(s -> s >= 50)
    .map(s -> s >= 80 ? "A" : s >= 65 ? "B" : "C")
    .orElse("FAIL");

Q9 — Optional flatMap (chaining Optional-returning methods)

java

String postal = findStudent("A123")
    .flatMap(s -> getAddress(s))
    .map(a -> a.getPostalCode())
    .orElse("UNKNOWN");

Q10 — Generic class with bounded type parameter

java

class Box<T extends Comparable<T>> {
    private final T item;
 
    Box(T item) {
        this.item = item;
    }
 
    public boolean isLargerThan(Box<T> other) {
        return this.item.compareTo(other.item) > 0;
    }
}

compareTo → boolean conversions:

WantWrite
a > ba.compareTo(b) > 0
a < ba.compareTo(b) < 0
a == ba.compareTo(b) == 0
a >= ba.compareTo(b) >= 0
a <= ba.compareTo(b) <= 0

Q11 — Functional interface + lambda

java

@FunctionalInterface
interface Transformer<T, R> {
    R transform(T input);
}
 
Transformer<String, Integer> len = s -> s.length();
// usage: len.transform("hello")  →  5

Q12 — Custom exception + try/catch/throw (translating exceptions)

java

// Custom unchecked exception
class BadNumberException extends RuntimeException {
    BadNumberException(String message) {
        super(message);
    }
}
 
// Method that catches and translates
int parseAndDouble(String s) {
    try {
        int x = Integer.parseInt(s);           // throws NumberFormatException
        return x * 2;
    } catch (NumberFormatException e) {
        throw new BadNumberException("Cannot parse: " + s);
    }
}

Q13 — Checked exception with throws clause

w java

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
 
int readInt(String filename) throws FileNotFoundException {
    Scanner sc = new Scanner(new File(filename));
    return sc.nextInt();
}

Throws rules:

  • Only checked exceptions need to be declared (extends Exception but not RuntimeException)
  • Multiple exceptions: throws IOException, InterruptedException
  • Overriding methods can throw fewer or more specific exceptions, never broader

Q14 — Stream.iterate + limit

java

List<Integer> powers = Stream.iterate(1, x -> x * 2)
    .limit(10)
    .toList();
// [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

Infinite stream builders:

java

Stream.iterate(seed, op)              // seed, op(seed), op(op(seed)), ...
Stream.iterate(seed, cond, op)        // with stop condition (Java 9+)
Stream.generate(() -> 1)              // repeated supplier call

Always .limit(n) before a terminal op, unless using the 3-arg iterate.


Q15 — Stream flatMap (flattening nested lists)

java

List<Integer> flattened = nested.stream()
    .flatMap(x -> x.stream())
    .toList();
 
// Or with method reference:
List<Integer> flattened = nested.stream()
    .flatMap(List::stream)
    .toList();

map vs flatMap rule:

  • map(f) when f returns a plain value
  • flatMap(f) when f returns a Stream (or Optional, Maybe, Lazy — same principle for each box)

Q16 — Parallel stream with reduce

java

int sum = numbers.stream()
    .parallel()
    .mapToInt(x -> x * x)
    .reduce(0, (x, y) -> x + y);
 
// Equivalent:
int sum = numbers.parallelStream()
    .mapToInt(x -> x * x)
    .reduce(0, Integer::sum);

Parallel correctness rules:

  1. No interference — don’t modify source mid-stream
  2. Stateless lambdas — no shared read/write state
  3. No side effects — use collect/toList, not forEach(list::add)
  4. Associative accumulator for reduce(a op b) op c == a op (b op c) must hold