← J.P. Morgan Interview Insights

J.P. Morgan·Software Engineer·Technical Phone Screen·Senior

SeniorPrefer not to say
Apr 2026

Summary

Technical phone screen for a backend Software Engineer role at J.P. Morgan, focused almost entirely on Java internals. Two main areas: concurrent collections and Java 8 functional features. Felt like the interviewer wanted real mechanics, not buzzwords.

Questions Asked (7)

Q1

What are the key differences between HashMap and ConcurrentHashMap, and how does ConcurrentHashMap allow concurrent access without locking the entire structure?

Technical Trade-offsSystem Design
Author's notes

I started with the thread-safety angle which was the right call.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by contrasting HashMap and ConcurrentHashMap in terms of thread safety, performance, and use cases. Then explain ConcurrentHashMap's internal design, focusing on how it achieves concurrency without global locking, such as through lock striping or CAS operations. Conclude with practical implications for high-concurrency systems.

Pro tip: Mention that ConcurrentHashMap does not allow null keys or values, unlike HashMap, and explain the rationale: to avoid ambiguity in concurrent retrieval operations. This shows attention to detail and understanding of design trade-offs.

1. Define HashMap and its limitations

Briefly describe HashMap as a non-thread-safe, high-performance map that requires external synchronization for concurrent use. Mention that it allows null keys and values.

2. Introduce ConcurrentHashMap and its advantages

Explain that ConcurrentHashMap is designed for concurrent access, providing thread safety without locking the entire map. Highlight its higher throughput compared to synchronized maps.

3. Explain concurrency mechanism: lock striping (Java 7) and CAS + synchronized (Java 8+)

For Java 7, describe segmentation where the map is divided into segments, each with its own lock. For Java 8+, explain that it uses CAS operations for insertions and synchronized blocks on individual bins (nodes) for updates, minimizing contention.

4. Discuss key differences and trade-offs

Compare performance, scalability, and memory overhead. Mention that ConcurrentHashMap does not allow nulls and has weaker iterators (fail-safe vs fail-fast).

5. Relate to real-world scenarios

Give examples where ConcurrentHashMap is preferred, such as in high-concurrency caches or shared registries, and note that HashMap is suitable for single-threaded or read-only contexts.

Key Points to Mention

  • Thread safety: HashMap is not thread-safe; ConcurrentHashMap is.
  • Locking granularity: ConcurrentHashMap uses finer-grained locking (segments or bins) instead of a single lock.
  • Java 8+ changes: Use of CAS and synchronized on individual bins, and tree bins for improved performance.
  • Null handling: ConcurrentHashMap prohibits null keys/values; HashMap allows them.
  • Iterators: ConcurrentHashMap iterators are weakly consistent (fail-safe), while HashMap iterators are fail-fast.
  • Performance: ConcurrentHashMap offers better concurrency and scalability under high contention.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

How do putIfAbsent and computeIfAbsent fix the race condition in a check-then-act sequence on a ConcurrentHashMap, and what atomicity does each actually guarantee?

Technical Trade-offsAlgorithms & Data Structures
Author's notes

This follow-up tripped me up more than I expected.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by explaining the check-then-act race condition in ConcurrentHashMap and how putIfAbsent and computeIfAbsent eliminate it by performing the check and action atomically. Then compare their atomicity guarantees, noting that putIfAbsent is atomic only for the put operation, while computeIfAbsent atomically computes and inserts, but the mapping function may be called multiple times under contention. Finally, discuss when to use each and the implications for side-effect-free functions.

Pro tip: Emphasize that computeIfAbsent's mapping function should be side-effect-free and fast, as it may be invoked multiple times if there's contention, and that putIfAbsent returns the existing value, which can be used to avoid unnecessary object creation.

1. Describe the race condition

Explain the check-then-act sequence: a thread checks if a key is absent, then another thread inserts the key, leading to duplicate insertions or overwrites. This is a classic race condition in concurrent programming.

2. Explain putIfAbsent

putIfAbsent atomically checks if the key is absent and only then puts the value, preventing the race. It guarantees atomicity for the check-and-put operation, but the value is computed beforehand, which may be wasteful.

3. Explain computeIfAbsent

computeIfAbsent atomically checks if the key is absent and if so, computes the value using the provided function and inserts it. It guarantees that the computation and insertion are atomic with respect to other map operations, but the function may be called multiple times under contention.

4. Compare atomicity guarantees

putIfAbsent guarantees that the put is atomic, but the value is already computed. computeIfAbsent guarantees that the mapping function is executed atomically with respect to other map updates, but the function itself may be invoked multiple times if there is contention, so it must be idempotent and side-effect-free.

5. Discuss trade-offs and use cases

Use putIfAbsent when the value is cheap to compute or already available. Use computeIfAbsent when the value is expensive to compute and you want to avoid unnecessary computation, but ensure the function is safe for multiple invocations.

Key Points to Mention

  • Check-then-act race condition: two threads may both see the key absent and both attempt to insert.
  • putIfAbsent is atomic: it checks and puts in one atomic operation, returning the existing value if present.
  • computeIfAbsent is atomic: it checks and computes/inserts atomically, but the mapping function may be called multiple times under contention.
  • The mapping function in computeIfAbsent should be side-effect-free and idempotent to handle multiple invocations.
  • putIfAbsent may waste resources by computing the value even if not needed.
  • Both methods are thread-safe and prevent the race condition, but they differ in when the value is computed and the guarantees around the computation.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

Why does ConcurrentHashMap disallow null keys and values when HashMap permits them?

Technical Trade-offs
Author's notes

Short answer: ambiguity.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by acknowledging the design difference: HashMap is not thread-safe and allows nulls, while ConcurrentHashMap is designed for concurrent use and explicitly forbids nulls. Explain that the primary reason is to avoid ambiguity in concurrent operations, particularly with methods like get() and containsKey(), where a null return could mean either 'no mapping' or 'mapped to null'. Then discuss how allowing nulls would complicate non-blocking algorithms and atomic operations, making the implementation error-prone and less efficient.

Pro tip: Mention that this is a deliberate design choice by Doug Lea to prevent race conditions and simplify concurrent code; showing awareness of the Java Memory Model and the trade-offs between flexibility and thread safety will impress interviewers.

1. State the core difference

Clearly state that ConcurrentHashMap prohibits null keys and values, unlike HashMap, and that this is intentional for concurrency.

2. Explain the ambiguity problem

Describe how in a concurrent environment, a null return from get() could be ambiguous: it could mean the key is absent or the value is null, leading to race conditions.

3. Discuss atomicity and non-blocking algorithms

Explain that ConcurrentHashMap uses lock-free reads and fine-grained locking for writes; allowing nulls would require additional checks and could break atomicity of operations like putIfAbsent.

4. Contrast with HashMap

Note that HashMap is not thread-safe, so it can afford to allow nulls without worrying about concurrent ambiguity or atomicity.

5. Conclude with design trade-off

Summarize that the restriction is a deliberate trade-off to ensure thread safety, simplicity, and performance in concurrent scenarios.

Key Points to Mention

  • Ambiguity of null return in concurrent get() and containsKey()
  • Atomic operations like putIfAbsent and computeIfAbsent require non-null values
  • Non-blocking algorithms and lock-free reads in ConcurrentHashMap
  • HashMap is not thread-safe, so nulls are permissible
  • Design choice by Doug Lea to avoid race conditions
  • Java Memory Model and visibility guarantees

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

How do you use lambda expressions in practice? Walk through a concrete before-and-after transformation.

Technical Trade-offs
Author's notes

Easy one.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Choose a concrete, real-world example from your experience where you replaced verbose anonymous classes or loops with lambda expressions. Walk through the before and after code, explaining the readability and maintainability improvements, and discuss any trade-offs like performance or debugging complexity. Keep it concise and focused on the transformation's impact.

Pro tip: Mention that while lambdas improve conciseness, they can obscure stack traces and complicate debugging; show you understand when to avoid them, such as in complex logic or when checked exceptions are involved. This demonstrates maturity and trade-off awareness, which is highly valued at J.P. Morgan.

1. Set the context

Briefly describe the problem or code scenario where you used lambdas, such as sorting a list or handling events, to ground your example.

2. Show the 'before' code

Present the original verbose implementation using anonymous inner classes or explicit loops, highlighting its drawbacks like boilerplate and reduced readability.

3. Show the 'after' code

Demonstrate the refactored version using lambda expressions, pointing out how it simplifies the code and improves clarity.

4. Explain the benefits and trade-offs

Discuss advantages like conciseness and functional style, but also mention potential downsides such as debugging challenges or performance considerations.

5. Conclude with impact

Summarize how this transformation improved the codebase, e.g., easier maintenance or fewer lines of code, and relate it to broader engineering principles.

Key Points to Mention

  • Concrete example from your experience (e.g., using lambdas with Java's Stream API or event listeners)
  • Before-and-after code comparison to illustrate the transformation
  • Readability and maintainability improvements
  • Trade-offs: debugging complexity, performance overhead, and when to avoid lambdas
  • Functional interfaces and type inference
  • Alignment with team coding standards and code review practices

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

What are the most common misuses of the Streams API, particularly around laziness, short-circuiting, and parallel streams with shared mutable state?

Technical Trade-offsAlgorithms & Data Structures
Author's notes

The parallel stream + shared ArrayList question is where things got interesting.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Structure your answer by first defining the three concepts (laziness, short-circuiting, parallel streams with shared mutable state) and then for each, describe common misuses and their consequences. Use concrete code examples or scenarios to illustrate, and emphasize how to avoid these pitfalls in production code.

Pro tip: Mention that while parallel streams can improve performance, they are often misused in I/O-bound or stateful operations, and that the overhead of parallelization can outweigh benefits for small datasets. Also, note that side effects in stream operations violate functional purity and can lead to non-deterministic bugs.

1. Define the concepts

Briefly explain laziness (intermediate operations are not executed until a terminal operation is invoked), short-circuiting (operations like findFirst or anyMatch can terminate early), and parallel streams (using multiple threads for processing).

2. Identify common misuses of laziness

Discuss how developers might expect side effects to occur during intermediate operations, or how they might reuse a stream after a terminal operation, leading to IllegalStateException.

3. Explain short-circuiting pitfalls

Highlight that short-circuiting can cause unexpected behavior if operations have side effects, or if developers assume all elements are processed (e.g., using peek for logging and missing elements).

4. Address parallel streams with shared mutable state

Describe how using shared mutable state (e.g., modifying a shared list) in parallel streams leads to race conditions, non-deterministic results, and performance degradation due to contention.

5. Provide best practices and alternatives

Suggest using stateless operations, avoiding side effects, using collectors instead of shared mutable state, and considering sequential streams or other concurrency constructs when appropriate.

Key Points to Mention

  • Laziness: Intermediate operations are lazy; terminal operations trigger execution. Misuse: expecting side effects during intermediate operations or reusing streams.
  • Short-circuiting: Operations like findFirst, anyMatch, limit can stop early. Misuse: relying on side effects in peek or forEach, which may not process all elements.
  • Parallel streams: Not always faster; overhead for small data or I/O-bound tasks. Misuse: using parallel() indiscriminately.
  • Shared mutable state: Causes race conditions and non-deterministic behavior. Use collectors or reduce with immutable accumulators.
  • Side effects: Avoid stateful lambda expressions; they break functional purity and can cause bugs in parallel execution.
  • Performance considerations: Parallel streams use ForkJoinPool; blocking operations can starve the pool. Measure before optimizing.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q6

How do you use Optional correctly, and what are the situations where it should not be used?

Technical Trade-offs
Author's notes

Went with the nested null-check to Optional.map chain transformation.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining Optional as a container for a value that may be absent, emphasizing its intended use as a return type for methods that might not produce a result. Then discuss correct usage patterns (e.g., avoiding null checks, using map/flatMap/orElse) and clearly outline anti-patterns such as using Optional for fields, method parameters, or collections. Conclude with trade-offs in performance and API design, especially in enterprise contexts like J.P. Morgan.

Pro tip: Mention that Optional is not Serializable, which is critical in distributed systems like those at J.P. Morgan, and that overusing Optional can lead to unnecessary object creation and complexity.

1. Define Optional and its purpose

Explain that Optional is a container object introduced in Java 8 to represent a value that may or may not be present, primarily intended as a method return type to signal possible absence.

2. Describe correct usage patterns

Highlight best practices: use Optional only for return types, avoid calling get() directly, prefer methods like orElse, orElseGet, ifPresent, map, and flatMap to handle absence gracefully.

3. Identify anti-patterns and misuse

Discuss situations where Optional should not be used: as method parameters, fields, collections, or in performance-critical code; also avoid using it for primitive types due to boxing overhead.

4. Discuss trade-offs and alternatives

Explain the trade-offs: Optional improves readability and reduces NullPointerExceptions but adds overhead and can complicate serialization. Mention alternatives like null with annotations or dedicated result types.

5. Relate to real-world scenarios

Connect to enterprise contexts: in financial systems, serialization and performance matter, so Optional should be used judiciously, e.g., in service layer return types but not in DTOs or entities.

Key Points to Mention

  • Optional is primarily for return types, not for fields, parameters, or collections.
  • Avoid using Optional.get() without checking; use orElse, orElseGet, or ifPresent instead.
  • Optional is not Serializable, which is a concern in distributed systems.
  • Using Optional for primitives causes unnecessary boxing and performance overhead.
  • Optional can improve API clarity by explicitly indicating possible absence.
  • Overuse of Optional can lead to verbose code and additional object allocation.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q7

When would you choose Collections.synchronizedMap or CopyOnWriteArrayList over ConcurrentHashMap?

Technical Trade-offsSystem Design
Author's notes

synchronizedMap makes sense when you need consistent snapshots across the whole map and can tolerate coarse locking.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying that ConcurrentHashMap is the default choice for concurrent maps, but there are specific scenarios where Collections.synchronizedMap or CopyOnWriteArrayList are preferable. Structure your answer around trade-offs: locking granularity, iteration semantics, read/write patterns, and memory consistency. Use concrete examples to illustrate when each collection shines.

Pro tip: Mention that in low-contention scenarios, Collections.synchronizedMap can be simpler and sufficient, but be aware of its coarse locking. For CopyOnWriteArrayList, highlight its use in event listener lists where reads vastly outnumber writes, and note that it provides snapshot iterators that never throw ConcurrentModificationException.

1. Clarify the default

State that ConcurrentHashMap is generally the go-to for concurrent maps due to its fine-grained locking (or lock-free reads) and high throughput. This shows you understand the baseline.

2. Explain Collections.synchronizedMap

Describe that it wraps a map with a single mutex, making all operations synchronized. It's useful when you need a synchronized map but don't require the high concurrency of ConcurrentHashMap, or when you need to synchronize on the map for compound operations.

3. Explain CopyOnWriteArrayList

Describe that it creates a new copy of the underlying array on each mutation, making reads very fast and iteration safe without locking. It's ideal for read-heavy, write-rare scenarios like maintaining a list of listeners.

4. Compare trade-offs

Contrast the three: ConcurrentHashMap for high concurrency with weak iterators; synchronizedMap for low concurrency with fail-fast iterators requiring external synchronization; CopyOnWriteArrayList for read-heavy lists with snapshot iterators.

5. Give practical examples

Provide scenarios: use ConcurrentHashMap for a shared cache; use synchronizedMap for a small map accessed infrequently; use CopyOnWriteArrayList for a registry of event handlers.

Key Points to Mention

  • ConcurrentHashMap uses lock striping or CAS for high concurrency and does not lock the entire map.
  • Collections.synchronizedMap uses a single lock, causing contention; iterators must be manually synchronized.
  • CopyOnWriteArrayList is thread-safe without locking for reads, but writes are expensive due to array copying.
  • Iteration semantics: ConcurrentHashMap provides weakly consistent iterators; synchronizedMap requires external synchronization; CopyOnWriteArrayList provides snapshot iterators.
  • Use cases: ConcurrentHashMap for high-throughput concurrent maps; synchronizedMap for low-contention or when you need to synchronize on the map; CopyOnWriteArrayList for read-mostly lists like listener registries.
  • Memory consistency: CopyOnWriteArrayList guarantees that iterators see a snapshot of the list at the time of creation.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.