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 reassigned3. 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 neededFunctional 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 typesWhen: 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"); // inferredBounded 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
| Form | Subtype 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 wrong8. Enums & Wrapper Types
Wrapper boxing
Integer i = 4; // autoboxed
int j = i; // auto-unboxedEnum
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, notRuntimeException): must catch or declarethrows. 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); // 25Method 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 instanceCurrying (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 application12. Java’s Functional Interfaces (java.util.function)
| Interface | Method | Shape |
|---|---|---|
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 |
Runnable | run() | () → 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-safeChaining
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 onWhen: 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..10Intermediate (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.
sortedanddistinctneed a finite stream — never on an infinite source withoutlimitfirst.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/toListinstead offorEach(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 finishesThread 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 doneGetting the value
cf.get(); // throws InterruptedException + ExecutionException (checked)
cf.join(); // throws unchecked CompletionExceptionException 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
| Annotation | Use |
|---|---|
@Override | Method is meant to override a parent method |
@FunctionalInterface | Interface is meant to have exactly one abstract method |
@SuppressWarnings("unchecked") | Suppress unchecked cast warning (only when provably safe) |
@SafeVarargs | Generic 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
| Notation | Meaning |
|---|---|
S <: T | S is a subtype of T |
Circle <: Shape | Circle 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) unrelated | C is invariant (Java generics default) |
26. Common gotchas to remember
- Generics are invariant:
List<Integer>is NOT aList<Number>. Use wildcards. - Arrays are covariant:
Integer[]IS anObject[]. Can causeArrayStoreException. - Type erasure: cannot do
new T[],new Pair<String,Integer>[],obj instanceof Pair<String>— butPair<?>works. - Streams consume once: recreate for each pipeline.
- Captured variables must be effectively final in lambdas / local / anonymous classes.
- Autoboxing in streams is costly: prefer
IntStreamoverStream<Integer>for numeric work. equalsmust takeObjectto properly override — notCircle. Useinstanceofinside.- Overriding return type can be narrower (covariant return), parameters cannot change.
- Overloading = same name, different signature. Overriding = same descriptor (signature + return type).
- 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 textQ5 — 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:
| Want | Write |
|---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a == b | a.compareTo(b) == 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.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") → 5Q12 — 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
Exceptionbut notRuntimeException) - 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 callAlways .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)whenfreturns a plain valueflatMap(f)whenfreturns 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:
- No interference — don’t modify source mid-stream
- Stateless lambdas — no shared read/write state
- No side effects — use
collect/toList, notforEach(list::add) - Associative accumulator for
reduce—(a op b) op c == a op (b op c)must hold