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.
Streams: Which Operator?
Section titled “Streams: Which Operator?”| 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
Collections: Which Structure?
Section titled “Collections: Which Structure?”| 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
Concurrency Toolbox
Section titled “Concurrency Toolbox”| 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 |
CompletableFuturebeats plainFuturebecauseFuture.get()blocks and can’t be chained or combined;CompletableFuturesupports both plus exception handling.CompletableFuturedefaults toForkJoinPool.commonPool()unless you pass an explicitExecutor.volatileguarantees visibility and prevents reordering, but not atomicity (avolatilecounter with++is still a race condition).
Full reference: Concurrency and Core Internals
Spring: Stereotypes and Scope
Section titled “Spring: Stereotypes and Scope”| 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
ObjectProviderto get a fresh instance per call.
Full reference: Stereotypes, DI, and the IoC Container
@Transactional: Propagation and Isolation
Section titled “@Transactional: Propagation and Isolation”| 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)
JPA Fetch Types
Section titled “JPA Fetch Types”| 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
Exceptions: Checked vs Unchecked
Section titled “Exceptions: Checked vs Unchecked”| 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.
== vs equals() / String Pool One-Liners
Section titled “== vs equals() / String Pool One-Liners”==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
Key Takeaways
Section titled “Key Takeaways”- 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,
@Transactionalpropagation/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.