Skip to content

Java + Spring Boot Cheat Sheet

Read this in 10-15 minutes before a call. Every row is a decision rule (“use X when Y”), not an explanation. Follow the links only for what you’ve actually forgotten.

Need Use Notes
Transform each element 1:1 map() nums.stream().map(n -> n * n)
Flatten nested collections into one stream flatMap() list.stream().flatMap(List::stream)
Keep elements matching a condition filter()
Reduce to a single value with your own combiner reduce() reduce(0, Integer::sum)
Reduce into a collection/Map/String collect() Always paired with a Collectors.xxx
Split into groups by a key (many groups) Collectors.groupingBy()
Split into exactly two groups (true/false) Collectors.partitioningBy() Not groupingBy when the split is boolean
Group + immediately transform each group groupingBy + collectingAndThen The go-to pattern for “Top N per group” / “latest per group”
Count occurrences per key groupingBy(key, counting())
Convert a List to a Map Collectors.toMap(keyFn, valueFn)
Join strings with a delimiter Collectors.joining(",")
Get sum/avg/min/max in one traversal IntSummaryStatistics (summaryStatistics()) Avoid separate .min() + .max() calls - two traversals
First element in encounter order findFirst()
Any matching element, parallel-friendly findAny() No order guarantee, but faster on parallelStream()

Top-N-per-group pattern (the single most common senior Stream question):

products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.collectingAndThen(
Collectors.toList(),
list -> list.stream()
.sorted(Comparator.comparing(Product::getCreatedDate).reversed())
.limit(3)
.toList()
)
));

Swap Collectors.toList() + sort/limit for Collectors.maxBy(comparator) when you only need the single highest/latest per group (skip the sort entirely).

Gotchas:

  • Intermediate ops (filter/map/sorted) are lazy - nothing runs until a terminal op (collect/count/findFirst) is called.
  • A stream can only be consumed once - calling a terminal op twice throws IllegalStateException.
  • Parallel streams: no ordering guarantee, risk of race conditions on shared mutable state, and often slower on small datasets.

Full reference: Java 8 Streams Interview Study Guide

Need Use Why
Read-heavy, random access by index ArrayList O(1) get
Frequent insert/delete at known position LinkedList O(1) insert/delete at a known node, O(n) access
Key-value, no thread-safety needed HashMap
Key-value, must preserve insertion order LinkedHashMap e.g. frequency maps where output order matters
Key-value, thread-safe, high concurrency ConcurrentHashMap Never use Hashtable - it’s legacy and coarsely locked
HashMap Hashtable
Thread-safe No Yes (coarse, slow)
Null keys 1 allowed None
Null values Many allowed None

Full reference: Java Core Fundamentals Interview Q&A

Need Use
Run a fixed pool of background tasks ExecutorService (Executors.newFixedThreadPool(n))
Chain/compose async results without blocking CompletableFuture (.thenApply, .thenCombine, .exceptionally)
Wait for N async tasks to finish before continuing CountDownLatch
Producer/consumer handoff between threads BlockingQueue
Simple mutual exclusion synchronized
Need fairness, interruptibility, or tryLock ReentrantLock
  • CompletableFuture beats plain Future because Future.get() blocks and can’t be chained or combined; CompletableFuture supports both plus exception handling.
  • CompletableFuture defaults to ForkJoinPool.commonPool() unless you pass an explicit Executor.
  • volatile guarantees visibility and prevents reordering, but not atomicity (a volatile counter with ++ is still a race condition).

Full reference: Concurrency and Core Internals

Annotation Role
@Component Generic bean, no specific layer
@Service Business logic layer
@Repository DAO layer - also translates persistence exceptions
@Controller Web layer, returns views
@RestController @Controller + @ResponseBody - REST API
  • Default bean scope is singleton. Use @Scope("prototype") for stateful/temporary beans.
  • Injecting a prototype bean into a singleton only wires one instance at creation time - use ObjectProvider to get a fresh instance per call.

Full reference: Stereotypes, DI, and the IoC Container

Propagation Meaning
REQUIRED (default) Join existing transaction, or create one
REQUIRES_NEW Always start a new transaction (suspends any existing one)
SUPPORTS Use existing transaction if present, else run non-transactionally
NOT_SUPPORTED Always run without a transaction
MANDATORY Must run inside an existing transaction, else throws
NEVER Must NOT run inside a transaction, else throws
NESTED Nested transaction (savepoint) within the existing one
Isolation Prevents
READ_UNCOMMITTED Nothing - dirty reads allowed
READ_COMMITTED Dirty reads
REPEATABLE_READ Dirty reads + non-repeatable reads
SERIALIZABLE Dirty + non-repeatable + phantom reads (highest cost)

Concurrency problems in increasing severity: dirty read (reads uncommitted data) → non-repeatable read (same query, different result within one transaction) → phantom read (new rows appear on repeat).

Defaults: MySQL uses REPEATABLE_READ; PostgreSQL uses READ_COMMITTED.

Why @Transactional silently does nothing on a private method: private methods can’t be proxied at all (CGLIB can’t override them). And separately, even a public @Transactional method called from within the same class (self-invocation) bypasses the proxy entirely - only calls arriving from outside the class go through the proxy.

Full reference: Spring Boot Interview Questions (8 Years)

Relationship Default Fetch
@ManyToOne EAGER
@OneToOne EAGER
@OneToMany LAZY
@ManyToMany LAZY

Rule of thumb: “to-one” defaults to EAGER, “to-many” defaults to LAZY. Override explicitly when the default doesn’t match your access pattern - eager-loading a large collection you don’t always need causes N+1/over-fetching.

Full reference: JPA Persistence Fundamentals

Checked (must handle or declare) Unchecked (RuntimeException)
IOException NullPointerException
SQLException ArrayIndexOutOfBoundsException
IllegalArgumentException
IllegalStateException

Use try-with-resources for anything Closeable (streams, connections) instead of manual finally blocks.

  • == compares references for objects, values for primitives. equals() compares logical content (if overridden).
  • "abc" == "abc"true (string pool interning). new String("abc") == new String("abc")false.
  • Object.equals() defaults to == unless the class overrides it.

Full reference: Java Core Fundamentals Interview Q&A

  • This page is a decision-rule index, not a learning doc - when a rule doesn’t fully make sense, follow the link to the source doc rather than guessing.
  • The highest-frequency senior-level question shapes are: Streams Top-N-per-group, HashMap/ConcurrentHashMap internals, @Transactional propagation/isolation, and JPA fetch defaults - know these cold first.
  • Re-read this page top to bottom right before a call; it’s designed to take under 15 minutes.