Java 8 Interview Questions & Answers (2026)
These interviews test your grasp of Java 8's functional features, stream API, default methods, and new date‑time classes. Show you can write clean lambda expressions, reason about parallel streams, and avoid common pitfalls. Demonstrate depth by explaining trade‑offs, performance implications, and how you’d refactor legacy code to leverage Java 8.
24 questions · updated Aug 29, 2026
Quick facts
| Typical rounds | Phone screen, coding challenge, system design, on‑site technical deep‑dive |
| Core topics | Lambdas, Stream API, Optional, Date/Time, CompletableFuture, default methods |
| Success metric | Clear explanation, correct syntax, performance awareness, and ability to refactor existing code |
Questions
Beginner
What is a lambda expression and when would you use it?
A lambda expression is an anonymous function that provides a clear, concise way to represent a single‑method interface (functional interface). It removes boilerplate for inner classes, making code more readable. Interviewers expect you to mention the syntax (parameters -> body), functional interfaces like Runnable, and scenarios such as event handling or collection processing where passing behavior is needed.
(e) -> System.out.println(e)Explain the difference between map() and flatMap() in streams.
map() transforms each element to another object, preserving the stream structure, while flatMap() both transforms and flattens nested streams into a single stream. Use map() for one‑to‑one conversions and flatMap() when each element produces a collection or stream that must be merged, such as converting List<List<String>> to List<String>. Interviewers look for an example and awareness of lazy evaluation.
List<String> flat = lists.stream().flatMap(List::stream).collect(Collectors.toList());How does the Stream API achieve lazy evaluation?
Streams build a pipeline of intermediate operations that are not executed until a terminal operation triggers evaluation. Each element flows through the pipeline one at a time, allowing short‑circuiting and reduced memory usage. Interviewers want you to note that operations like filter, map, and sorted are lazy, while forEach, collect, or reduce are eager, and discuss how this impacts performance.
Stream.of(1,2,3).filter(i -> i>1).map(i -> i*2).findFirst();How does method reference differ from lambda expression?
Method references are a shorthand for lambdas that directly invoke an existing method. They improve readability when the lambda body merely forwards arguments. Syntax includes ::new for constructors, ClassName::staticMethod, and instance::instanceMethod. Interviewers expect you to show equivalent forms and discuss when a method reference is preferred for clarity.
list.forEach(System.out::println);What is the difference between Stream.of() and Arrays.stream()?
Stream.of() creates a stream from a varargs list of elements, while Arrays.stream() creates a stream directly from an array, preserving primitive specialization for int[], long[], double[]. Use Stream.of() for objects and when you have individual elements; use Arrays.stream() for array processing and to avoid boxing of primitives. Interviewers look for you to mention performance and type differences.
IntStream intStream = Arrays.stream(new int[]{1,2,3});How would you convert a List<String> to a comma‑separated String using streams?
Use the joining collector: list.stream().collect(Collectors.joining(", ")). This creates a single string with elements separated by commas, handling empty lists gracefully. Interviewers expect you to mention that joining is a terminal operation and that it avoids manual StringBuilder loops, improving readability.
String csv = list.stream().collect(Collectors.joining(", "));What is the purpose of the @FunctionalInterface annotation?
It signals that the interface is intended to be a functional interface with exactly one abstract method, enabling lambda assignment. The compiler enforces this constraint, providing early error detection. Interviewers may ask why it's optional and how it aids documentation and tooling.
@FunctionalInterface interface Converter<T,R> { R convert(T t); }Can you explain the difference between mapToInt and map?
mapToInt returns an IntStream, a primitive specialization that avoids boxing of int values, providing additional methods like sum, average, and max. map returns a Stream<T> and incurs boxing for primitive types. Interviewers look for you to discuss performance benefits and when to choose the primitive stream for numeric aggregations.
int total = list.stream().mapToInt(String::length).sum();Intermediate
What are default methods in interfaces and why were they introduced?
Default methods allow interfaces to provide a concrete implementation for a method, enabling backward compatibility when adding new methods to existing interfaces. They let developers evolve APIs without breaking existing implementations. Interviewers expect you to discuss the diamond problem, how Java resolves it by preferring the most specific implementation, and when using default methods is appropriate versus abstract classes.
interface MyInterface { default void log() { System.out.println("default"); } }When should you prefer a parallel stream over a sequential stream?
Use parallel streams for CPU‑bound, stateless operations on large collections where the overhead of thread management is outweighed by work distribution. Avoid them for small data sets, I/O‑bound tasks, or when order matters and the operation is not thread‑safe. Interviewers look for you to mention ForkJoinPool.commonPool(), the need to assess contention, and potential side‑effects of shared mutable state.
List<Integer> result = list.parallelStream().map(this::compute).collect(Collectors.toList());Describe how Optional helps avoid NullPointerException.
Optional is a container that may hold a non‑null value or be empty, forcing callers to explicitly handle the absent case. Methods like orElse, orElseGet, and ifPresent make the handling of missing values explicit, reducing accidental dereferencing. Interviewers expect you to demonstrate using Optional as a return type, not as a field, and to discuss its impact on API design and readability.
Optional<String> name = Optional.ofNullable(user.getName()); name.ifPresent(System.out::println);What is the new Date/Time API and how does it improve over java.util.Date?
Java 8 introduced java.time, a comprehensive, immutable, and thread‑safe API. Classes like LocalDate, LocalTime, and ZonedDateTime separate concerns of date, time, and timezone, eliminating the mutable, poorly designed java.util.Date and Calendar. Interviewers want you to mention ISO‑8601 compliance, fluent builders, and the ability to perform arithmetic without side effects.
LocalDate today = LocalDate.now(); LocalDate nextWeek = today.plusWeeks(1);Can you modify a collection while iterating with a stream?
No. Streams operate on a source snapshot; mutating the underlying collection during processing leads to ConcurrentModificationException or undefined behavior. Instead, collect results into a new collection or use forEachOrdered for ordered side‑effects. Interviewers look for you to stress immutability and functional style, and to suggest alternatives like map‑to‑list.
List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());What is a Collector and how would you create a custom one?
A Collector defines how to accumulate stream elements into a mutable result container, optionally finishing with a transformation. The three functions are supplier, accumulator, and combiner, plus characteristics. To create a custom collector, implement Collector<T,A,R> or use Collector.of(supplier, accumulator, combiner, finisher). Interviewers expect a simple example, such as grouping by a custom key or concatenating strings with a delimiter.
Collector<Employee, ?, Map<Dept, List<Employee>>> byDept = Collectors.groupingBy(Employee::getDept);What are the advantages of using immutable objects with streams?
Immutable objects guarantee thread safety, eliminating race conditions when processing in parallel streams. They also simplify reasoning about data flow, enable safe reuse, and align with functional programming principles. Interviewers look for you to discuss how immutability reduces the need for synchronization and leads to more predictable performance.
How does the Stream API support short‑circuiting operations?
Operations like anyMatch, allMatch, noneMatch, findFirst, findAny, and limit stop processing as soon as the result is determined. Because streams are lazy, the pipeline halts early, saving work. Interviewers expect you to illustrate with an example where a large dataset is filtered but processing stops after the first match.
boolean has = list.stream().filter(s -> s.startsWith("X")).findFirst().isPresent();What is a Stream's encounter order and how does it affect operations?
Encounter order is the order in which elements appear in the source. Ordered streams preserve this order through intermediate operations, while unordered streams may reorder for performance. Operations like sorted, limit, and findFirst depend on order. Interviewers expect you to explain how to remove order with unordered() to improve parallel performance.
Advanced
How does CompletableFuture differ from Future?
CompletableFuture extends Future with non‑blocking callbacks, composition, and explicit completion. It supports chaining via thenApply, thenCombine, and exception handling with exceptionally. Unlike Future, which blocks on get(), CompletableFuture enables asynchronous pipelines and can be manually completed. Interviewers look for you to discuss use cases like async I/O, handling timeouts, and the underlying ForkJoinPool.
CompletableFuture.supplyAsync(() -> fetch()).thenApply(this::process).exceptionally(e -> fallback());Explain the purpose of the Spliterator interface.
Spliterator is a counterpart to Iterator designed for parallel processing. It can split a source into independent sub‑spliterators, enabling efficient work distribution across threads. Methods trySplit, tryAdvance, and characteristics inform the stream framework about size, order, and mutability. Interviewers expect you to relate it to the internal workings of parallel streams and when custom spliterators are needed for non‑standard data structures.
Spliterator<String> spliterator = list.spliterator();What are the functional interfaces in java.util.function?
java.util.function defines core functional interfaces: Predicate<T> (boolean test), Function<T,R> (apply), Consumer<T> (accept), Supplier<T> (get), BiPredicate, BiFunction, BiConsumer, UnaryOperator, BinaryOperator, and their primitive specializations (IntPredicate, LongSupplier, etc.). Knowing these lets you choose the right type for lambda signatures, improving readability and type safety. Interviewers often ask you to map a use case to the appropriate interface.
Predicate<Integer> isEven = i -> i % 2 == 0;What is the difference between findFirst() and findAny() in parallel streams?
findFirst() respects encounter order and may incur additional synchronization, while findAny() allows the runtime to return any element, enabling better performance in parallel streams. Use findAny() when order is irrelevant. Interviewers expect you to discuss the trade‑off between determinism and speed, and how unordered collections benefit from findAny().
Optional<String> any = list.parallelStream().filter(s -> s.startsWith("A")).findAny();How does the Stream API handle checked exceptions?
Streams do not allow checked exceptions directly in lambda expressions. Common approaches include wrapping the exception in a RuntimeException, using utility methods that rethrow, or converting to an unchecked exception via a helper. Interviewers look for you to demonstrate a clean wrapper or use of try‑catch inside the lambda, emphasizing readability and proper exception propagation.
list.stream().map(s -> { try { return parse(s); } catch (ParseException e) { throw new RuntimeException(e); } }).collect(Collectors.toList());Explain the role of the ForkJoinPool in parallel streams.
Parallel streams use the common ForkJoinPool, which manages a pool of worker threads that recursively split tasks (using Spliterators) and join results. The pool size defaults to the number of available processors, providing efficient work‑stealing. Interviewers want you to discuss how you can override the pool via .parallel().submit() or System.setProperty, and the impact on CPU utilization.
list.parallelStream().map(this::compute).collect(Collectors.toList());Explain how to handle side effects in stream pipelines.
Side effects should be confined to terminal operations like forEach or peek, and kept minimal. Prefer pure functions in intermediate stages. When side effects are necessary, use peek for debugging, but ensure thread safety for parallel streams. Interviewers expect you to discuss why side effects break laziness and can cause nondeterministic results.
Common mistakes
- Using mutable state inside parallel streams, causing race conditions
- Forgetting to handle checked exceptions in lambda expressions
- Confusing map() with flatMap() leading to nested collections
- Relying on findFirst() in parallel streams when order is irrelevant
- Using Optional as a field, which defeats its purpose
Study plan
- Review Java 8 core concepts: lambdas, functional interfaces, and method references
- Practice stream pipelines: filter, map, flatMap, collect, and short‑circuiting
- Master the new Date/Time API and understand time‑zone handling
- Deep dive into CompletableFuture and parallel stream performance trade‑offs
- Solve 15 coding problems that require refactoring legacy loops to streams
FAQ
Do I need to know every method in java.util.function?
Focus on the most common ones—Predicate, Function, Consumer, Supplier, and their primitive variants. Knowing their signatures and typical use cases is enough; you can look up less‑frequent interfaces during an interview if needed.
Is it safe to use parallel streams for I/O operations?
Generally no. Parallel streams are optimized for CPU‑bound tasks. I/O operations can block threads, reducing throughput and causing contention. Use explicit asynchronous APIs like CompletableFuture for I/O.
How many lines of code should a lambda contain?
Ideally one expression; if you need multiple statements, use a block with braces, but consider extracting a method for readability. Interviewers appreciate concise lambdas that convey intent clearly.
Can I mix Stream API with traditional loops?
Yes, but keep the style consistent. Mixing can confuse readers and hide performance issues. Prefer streams for collection processing and loops for simple index‑based logic.
What is the best way to debug a stream pipeline?
Insert peek() after key stages to log intermediate values, or temporarily collect to a list for inspection. Remember that peek() should not have side effects beyond debugging.
Related
Ready for your next interview?
Download MiPrep AI. Load your resume and the job description. Show up ready.
Free tier · No credit card · macOS 14+ · Windows 10+
Free tier · No credit card · Runs on your Mac or Windows machine