What are the main features introduced in Java 8?
Lambdas, functional interfaces (
Function,Predicate,Supplier,Consumer), the Stream API, method references, default and static interface methods,Optional, andjava.time(LocalDate,Instant) replacingDateandCalendar. Metaspace also replaced PermGen.What is a functional interface?
An interface with exactly one abstract method, so a lambda or method reference can implement it; default and static methods do not count.
@FunctionalInterfacemakes the compiler enforce that.Javaimport java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @FunctionalInterface interface Discount { double apply(double price); } public class Main { public static void main(String[] args) { Discount tenOff = p -> p * 0.9; Predicate<String> isEmpty = String::isEmpty; Function<String, Integer> len = String::length; Supplier<String> hello = () -> "hello"; System.out.println(tenOff.apply(200)); System.out.println(isEmpty.test("") + " " + len.apply("java") + " " + hello.get()); } }It prints
180.0andtrue 4 hello. See lambda expressions.How do Java streams work? Show filter, map and reduce.
A stream is a source, lazy intermediate operations (
filter,map,sorted) and one terminal operation (collect,reduce,count) that runs the pipeline.Javaimport java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> nums = List.of(5, 3, 8, 1, 9, 2); List<Integer> evensSquared = nums.stream() .filter(n -> n % 2 == 0) .map(n -> n * n) .toList(); int sum = nums.stream().reduce(0, Integer::sum); String joined = nums.stream().sorted().map(String::valueOf) .collect(Collectors.joining("-")); System.out.println(evensSquared + " " + sum + " " + joined); } }It prints
[64, 4] 28 1-2-3-5-8-9. A stream is single use: a second terminal operation throwsIllegalStateException. More in the streams guide.Does a stream
filterrun without a terminal operation?Javaimport java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream<Integer> s = Stream.of(1, 2, 3).filter(x -> { System.out.print("f" + x + " "); return x > 1; }); System.out.println("done"); } }It prints only
done. Intermediate operations are lazy: with no terminal operation no element flows, so thefilterlambda never runs. The same laziness letsfindFirst()stop at the first match.In what order do
mapandforEachrun on a stream?Javaimport java.util.ArrayList; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> log = new ArrayList<>(); Stream.of("a", "b") .map(s -> { log.add("map " + s); return s; }) .forEach(s -> log.add("each " + s)); System.out.println(String.join(", ", log)); } }It prints
map a, each a, map b, each b. Each element goes through the whole pipeline before the next one enters.A stateful step changes that: with
sorted()beforeforEach, every element is mapped before any reachesforEach.What is the difference between
mapandflatMapin streams?mapgives one output per element;flatMapmaps each element to a stream and flattens the results into one stream.Javaimport java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> lines = List.of("to be", "or not"); List<Integer> lengths = lines.stream().map(String::length).toList(); List<String> words = lines.stream() .flatMap(line -> Arrays.stream(line.split(" "))) .toList(); System.out.println(lengths); System.out.println(words); } }It prints
[5, 6]and[to, be, or, not].How do you group elements with streams? Explain
Collectors.groupingBy.groupingBy(classifier)collects into aMapfrom each key to the list of elements with that key; a downstream collector likecounting()changes the values.Javaimport java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> words = List.of("java", "spring", "jvm", "stream", "lambda", "jdk"); Map<Integer, List<String>> byLength = words.stream() .collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.toList())); Map<Character, Long> countByFirst = words.stream() .collect(Collectors.groupingBy(w -> w.charAt(0), TreeMap::new, Collectors.counting())); System.out.println(byLength); System.out.println(countByFirst); } }It prints
{3=[jvm, jdk], 4=[java], 6=[spring, stream, lambda]}and{j=3, l=1, s=2}. The one-argument form promises no map type or order, henceTreeMap::new.Practice it: Group Anagrams
What is Optional? What is the difference between
orElseandorElseGet?Optional<T>is a return type for a value that may be absent.orElse(x)always evaluatesx;orElseGet(supplier)runs the supplier only when the Optional is empty.Javaimport java.util.Optional; public class Main { static String loadDefault() { System.out.println(" loadDefault() called"); return "guest"; } public static void main(String[] args) { Optional<String> user = Optional.of("asha"); System.out.println("orElse:"); String a = user.orElse(loadDefault()); System.out.println("orElseGet:"); String b = user.orElseGet(Main::loadDefault); System.out.println(a + " " + b); System.out.println(Optional.<String>empty().map(String::toUpperCase).orElse("none")); } }loadDefault() calledappears only afterorElse:, and the last two lines areasha ashaandnone. Do not useOptionalfor fields or parameters. See Optional.What happens when
Collectors.toMapmeets a duplicate key?It throws
IllegalStateException. Pass a merge function as the third argument to decide what the key keeps, and a map supplier as the fourth to choose the map type.Javaimport java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> words = List.of("apple", "avocado", "banana"); try { words.stream().collect(Collectors.toMap(w -> w.charAt(0), w -> w)); } catch (IllegalStateException e) { System.out.println(e.getMessage()); } Map<Character, String> merged = words.stream() .collect(Collectors.toMap(w -> w.charAt(0), w -> w, (a, b) -> a + "+" + b, // merge function for duplicates TreeMap::new)); System.out.println(merged); } }It prints
Duplicate key a (attempted merging values apple and avocado)and{a=apple+avocado, b=banana}.toMapalso throwsNullPointerExceptionwhen a value is null.When should you use a parallel stream, and when should you avoid it?
Use one for CPU-heavy work on large data that splits easily (arrays,
ArrayList,IntStream.range) with no shared mutable state. Avoid it for small inputs, blocking I/O,LinkedListorStream.iteratesources, and lambdas that write to shared collections.Javaimport java.util.List; import java.util.stream.IntStream; public class Main { public static void main(String[] args) { long sum = IntStream.rangeClosed(1, 1_000_000).parallel().asLongStream().sum(); List<Integer> doubled = List.of(1, 2, 3, 4).parallelStream().map(n -> n * 2).toList(); System.out.println(sum); System.out.println(doubled); // toList keeps encounter order } }It prints
500000500000and[2, 4, 6, 8]:toList()keeps encounter order, whileforEachon a parallel stream does not. All parallel streams share the commonForkJoinPool, so one slow task slows the others. Measure before switching.
Java 8 Interview Questions
Lambdas, functional interfaces, the Stream API and Optional, including how streams evaluate.