Interview

15 Java Streams Interview Questions and Answers

Prepare for your next Java interview with our guide on Java Streams. Enhance your skills with curated questions and answers for effective data processing.

Java Streams, introduced in Java 8, revolutionized the way developers handle collections and data processing. By providing a functional approach to operations on sequences of elements, Java Streams enable more readable, concise, and efficient code. This powerful feature is essential for modern Java development, allowing for parallel processing and complex data manipulation with ease.

This article offers a curated selection of interview questions focused on Java Streams, designed to help you demonstrate your proficiency and understanding of this critical feature. By familiarizing yourself with these questions and their answers, you’ll be better prepared to showcase your expertise and problem-solving abilities in your next technical interview.

Java Streams Interview Questions and Answers

1. Write a stream operation that filters out all even numbers from a list and then maps each remaining number to its square.

To filter out all even numbers from a list and then map each remaining number to its square using Java Streams, you can use the filter and map methods. The filter method excludes elements that do not match a given predicate, and the map method transforms each element in the stream.

Example:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        List<Integer> result = numbers.stream()
                                      .filter(n -> n % 2 != 0)
                                      .map(n -> n * n)
                                      .collect(Collectors.toList());

        System.out.println(result);
    }
}

2. Use a stream to collect all unique words from a list of sentences into a set.

To collect all unique words from a list of sentences into a set, use streams to process collections in a functional style. You can use flatMap to split sentences into words and collect to gather them into a set.

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class UniqueWords {
    public static void main(String[] args) {
        List<String> sentences = Arrays.asList(
            "This is a sentence",
            "This is another sentence",
            "And this is yet another sentence"
        );

        Set<String> uniqueWords = sentences.stream()
            .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
            .collect(Collectors.toSet());

        System.out.println(uniqueWords);
    }
}

3. Sort a list of employees by their salary in descending order using streams.

To sort a list of employees by their salary in descending order, use the sorted method with a comparator.

Example:

import java.util.*;
import java.util.stream.*;

class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "Employee{name='" + name + "', salary=" + salary + "}";
    }
}

public class Main {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("John", 50000),
            new Employee("Jane", 60000),
            new Employee("Doe", 55000)
        );

        List<Employee> sortedEmployees = employees.stream()
            .sorted(Comparator.comparingDouble(Employee::getSalary).reversed())
            .collect(Collectors.toList());

        sortedEmployees.forEach(System.out::println);
    }
}

4. Transform a list of lists of integers into a single list containing all integers using streams.

To transform a list of lists of integers into a single list, use the flatMap method to flatten the nested lists.

Example:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FlattenList {
    public static void main(String[] args) {
        List<List<Integer>> listOfLists = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5, 6),
            Arrays.asList(7, 8, 9)
        );

        List<Integer> flattenedList = listOfLists.stream()
            .flatMap(List::stream)
            .collect(Collectors.toList());

        System.out.println(flattenedList);
    }
}

5. Use a stream to sum all the integers in a list.

To sum all the integers in a list using a stream, use the reduce method, which combines elements using a binary operator.

Example:

import java.util.Arrays;
import java.util.List;

public class SumUsingStreams {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int sum = numbers.stream().reduce(0, Integer::sum);
        System.out.println("Sum: " + sum);
    }
}

6. Convert a sequential stream to a parallel stream and explain when you might want to do this.

To convert a sequential stream to a parallel stream, use the parallel() method. This can be beneficial for large datasets or computationally intensive operations, but be aware of potential overhead and thread safety issues.

Example:

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Sequential Stream
        numbers.stream()
               .forEach(System.out::println);

        // Parallel Stream
        numbers.stream()
               .parallel()
               .forEach(System.out::println);
    }
}

7. Implement a custom collector that concatenates strings with a delimiter.

To implement a custom collector that concatenates strings with a delimiter, define the supplier, accumulator, combiner, and finisher methods.

Example:

import java.util.stream.Collector;
import java.util.function.Supplier;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.Set;
import java.util.HashSet;

public class StringConcatenator implements Collector<String, StringBuilder, String> {
    private final String delimiter;

    public StringConcatenator(String delimiter) {
        this.delimiter = delimiter;
    }

    @Override
    public Supplier<StringBuilder> supplier() {
        return StringBuilder::new;
    }

    @Override
    public BiConsumer<StringBuilder, String> accumulator() {
        return (sb, s) -> {
            if (sb.length() > 0) {
                sb.append(delimiter);
            }
            sb.append(s);
        };
    }

    @Override
    public BinaryOperator<StringBuilder> combiner() {
        return (sb1, sb2) -> {
            if (sb1.length() > 0 && sb2.length() > 0) {
                sb1.append(delimiter);
            }
            sb1.append(sb2);
            return sb1;
        };
    }

    @Override
    public Function<StringBuilder, String> finisher() {
        return StringBuilder::toString;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return new HashSet<>();
    }

    public static void main(String[] args) {
        String result = java.util.Arrays.asList("a", "b", "c")
            .stream()
            .collect(new StringConcatenator(", "));
        System.out.println(result); // Output: a, b, c
    }
}

8. How would you handle checked exceptions within a stream pipeline?

To handle checked exceptions within a stream pipeline, use a wrapper method that catches the checked exception and rethrows it as an unchecked exception.

Example:

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class StreamExceptionHandling {

    public static void main(String[] args) {
        List<String> data = Arrays.asList("1", "2", "a", "4");

        List<Integer> result = data.stream()
            .map(wrapper(StreamExceptionHandling::parseInt))
            .collect(Collectors.toList());

        System.out.println(result);
    }

    public static Integer parseInt(String s) throws NumberFormatException {
        return Integer.parseInt(s);
    }

    public static <T, R> Function<T, R> wrapper(CheckedFunction<T, R> checkedFunction) {
        return i -> {
            try {
                return checkedFunction.apply(i);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
        R apply(T t) throws Exception;
    }
}

9. Group a list of people objects by their age using streams.

To group a list of people objects by their age, use the Collectors.groupingBy method.

Example:

import java.util.*;
import java.util.stream.Collectors;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 30),
            new Person("Bob", 25),
            new Person("Charlie", 30),
            new Person("David", 25)
        );

        Map<Integer, List<Person>> groupedByAge = people.stream()
            .collect(Collectors.groupingBy(Person::getAge));

        groupedByAge.forEach((age, persons) -> {
            System.out.println("Age: " + age);
            persons.forEach(person -> System.out.println(" - " + person.getName()));
        });
    }
}

10. Partition a list of integers into even and odd numbers using streams.

To partition a list of integers into even and odd numbers, use the partitioningBy collector.

Example:

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class PartitionExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        Map<Boolean, List<Integer>> partitioned = numbers.stream()
            .collect(Collectors.partitioningBy(n -> n % 2 == 0));

        List<Integer> evenNumbers = partitioned.get(true);
        List<Integer> oddNumbers = partitioned.get(false);

        System.out.println("Even Numbers: " + evenNumbers);
        System.out.println("Odd Numbers: " + oddNumbers);
    }
}

11. Concatenate two streams of integers into a single stream.

To concatenate two streams of integers into a single stream, use the Stream.concat method.

Example:

import java.util.stream.Stream;

public class StreamConcatenation {
    public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.of(1, 2, 3);
        Stream<Integer> stream2 = Stream.of(4, 5, 6);

        Stream<Integer> concatenatedStream = Stream.concat(stream1, stream2);

        concatenatedStream.forEach(System.out::println);
    }
}

12. Use an IntStream to calculate the average of a list of integers.

To calculate the average of a list of integers using IntStream, use the average() method, which returns an OptionalDouble.

Example:

import java.util.Arrays;
import java.util.List;
import java.util.OptionalDouble;
import java.util.stream.IntStream;

public class AverageCalculator {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        OptionalDouble average = numbers.stream()
                                        .mapToInt(Integer::intValue)
                                        .average();

        average.ifPresent(System.out::println);
    }
}

13. Parallel Stream Performance Considerations: When should you use parallel streams, and what are the potential pitfalls?

Parallel streams can improve performance for certain tasks by leveraging multiple CPU cores. They are useful for CPU-bound operations where the workload can be divided into independent subtasks. However, consider the following:

  • Task Suitability: Best for tasks that can be divided into independent subtasks.
  • Overhead: Managing multiple threads introduces overhead, which may outweigh benefits for small datasets.
  • Thread Contention: Shared resources can lead to performance degradation.
  • Order Sensitivity: Parallel streams may not maintain element order.

Example:

import java.util.Arrays;
import java.util.List;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // Using parallel stream
        int sum = numbers.parallelStream()
                         .mapToInt(Integer::intValue)
                         .sum();

        System.out.println("Sum: " + sum);
    }
}

14. Describe short-circuiting operations in streams and provide examples.

Short-circuiting operations in streams allow processing to terminate early when a condition is met, optimizing performance.

Examples include:

  • findFirst: Returns the first element and terminates processing.
  • findAny: Returns any element, useful in parallel streams.
  • anyMatch: Returns true if any elements match the predicate.
  • allMatch: Returns true if all elements match the predicate.
  • noneMatch: Returns true if no elements match the predicate.

Example:

import java.util.Arrays;
import java.util.List;

public class ShortCircuitingExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // findFirst example
        Integer first = numbers.stream()
                               .filter(n -> n > 3)
                               .findFirst()
                               .orElse(null);
        System.out.println("First number greater than 3: " + first);

        // anyMatch example
        boolean anyMatch = numbers.stream()
                                  .anyMatch(n -> n > 8);
        System.out.println("Any number greater than 8: " + anyMatch);
    }
}

15. How would you combine multiple stream operations efficiently to minimize performance overhead?

To combine multiple stream operations efficiently, understand the difference between intermediate and terminal operations. Intermediate operations are lazy and do not process the stream until a terminal operation is invoked, allowing for efficient processing.

Example:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

List<String> result = names.stream()
    .filter(name -> name.startsWith("A"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

In this example, filter and map are intermediate operations, and collect is a terminal operation. By combining these operations in a single pipeline, the stream processes each element only once, reducing overhead.

Previous

10 VLSI Analog Layout Interview Questions and Answers

Back to Interview