Illustration of Java Streams and Lambda Expressions used in functional programming

Explain the concept of Parallel Streams.
Answer: Parallel Streams parallelize the processing of data across the CPU cores and hence increase the performance for large data sets. In case elements in a stream are processed in parallel each chunk from the elements is processed independently in different threads.
Parallel streams should be used with caution since they introduce overhead and might not necessarily give a performance improvement, especially for smaller collections.
Example
import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample{
public static void main(String[] args) {
List numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
} .reduce(0, (a, b) -> a + b);
System.out.println("Parallel sum: " + sum); // prints 15
}
}
This shows the use of parallelStream() to sum the elements in parallel.


9.What are Optional values in Java Streams?

Answer: Optional is a container object used to represent a value that may or may not be present. Streams often use Optional to represent the result of an operation where the result might not exist (e.g., searching for an element that may not be in the list).
Common methods of Optional include ifPresent(), orElse(), and get().
Example:
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class OptionalExample{ public static void main(String[] args) {
List words = Arrays.asList("apple", "banana", "cherry");
Optional result = words.stream()}
filter(w -> w.startsWith("x"))
.findFirst();
result.ifPresent(System.out::println); // Nothing is printed
System.out.println(result.orElse("No match found")); // prints "No match found"
}
}
In this example, Optional is used to safely handle the result of the stream avoiding null references.


10.What does forEach() do in Streams?
Answer: Java Streams forEach() is a terminal operation that iterates over the elements in the stream. It takes a Consumer functional interface which processes each element in the stream.
Example:
import java.util.Arrays;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Doe");
names.stream()
.forEach(name -> System.out.println("Name: " + name));
}
}
Here, forEach() prints every element in the stream.


11.Why was the method called collect() in Java Streams?
Answer: In Java Streams, the collect() method is a terminal operation that gathers together the elements of a stream into a collection or some other form and transforms the stream into another form, possibly as a List, Set, Map or indeed any other collection. It falls under the Collector interface, one of the most frequently used kinds of final operations within Streams.
Example:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class CollectExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Even Numbers: " + evenNumbers); // prints [2, 4]
}
}
In the example below, collect() gathers the even numbers from the stream into a List, by using Collectors.toList().


12.Difference between map() and flatMap() in Java Streams?
Answer: Both map() and flatMap() apply transformations to the elements of a stream, however they work slightly differently.
•map() is applicable when you want each element in your stream to transform into another (one-to-one mapping). A function is supplied as an argument, which the method applies on each element.
•flatMap() is used when the transformation yields a stream of elements for each input element (one-to-many mapping). It flattens the resulting streams into a single stream.
Example:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FlatMapExample{
public static void main(String[] args) {
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5),
Arrays.asList(6, 7, 8)
)
List<Integer> flattened = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flattened); /* prints [1, 2, 3, 4, 5, 6, 7, 8] */
}
In the following example, flatMap() is applied in order to flatten a list of lists into one list.


13.What does the distinct() method in Java Streams do?
Answer: The distinct() method in Java Streams will remove all duplicate elements in the stream. It then returns a stream with unique elements based on the equals() method where it tries to compare the elements.
Example:
import java.util.Arrays;
import java.util.List;
public class DistinctExample{
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 4, 3, 5);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.toList();
System.out.println(distinctNumbers); /* prints [1, 2, 3, 4, 5] */
}
}
In this example, the distinct() method removes duplicates from the list.


14.How do you handle exceptions in Streams?
Answer: It is a bit tricky to handle exceptions in Streams because the operations in Streams are designed to be functional and clean. However, exceptions can occur in operations like map() or filter(). You can either catch the exception inside the lambda expression or use custom wrapper functions to manage exceptions.
Example:
import java.util.Arrays;
import java.util.List;
public class ExceptionHandlingExample {
public static void main(String[] args) {
List numbers = Arrays.asList("1", "2", "three", "4");
numbers.stream()
.map(num ->
try
{ return Integer.parseInt(num);
} catch (NumberFormatException e) {
return 0; /* Return 0 if invalid number */
}
})
.forEach(System.out::println); /* prints 1 2 0 4 */
}
In this example, we pass default value 0 to handle the NumberFormatException when an invalid number is encountered.


15.What is the difference between findFirst() and findAny() in Java Streams?
Answer: Both findFirst() and findAny() are terminal operations in Streams that return an Optional containing the first or any element from the stream.
•findFirst() is sure to return the first element in the stream, regardless of whether it is processed sequentially or in parallel.
•findAny() returns any element from the stream. Using parallel streams, findAny() can return any element based on the order of processing.
Example:
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class FindExample{
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> first = numbers.stream()
.findFirst();
Optional<Integer> any = numbers.stream()
.findAny();
System.out.println("First element: " + first.orElse(-1)); /* prints 1 */
System.out.println("Any element: " + any.orElse(-1)); /* prints 1 or any other element */
}
}
In the example above, findFirst() will return the first element and findAny() could return any of them.