Java 8 Interview Questions & Answers



StringJoiner Here is an example:

StringJoiner joiner =newStringJoiner("/");
joiner.add("usr");
joiner.add("local");
joiner.add("bin");

This will give you a string like "usr/local/bin" which you can pass it to any program.


String.join()

String colonSeparatedValue=String.join(":", "abc", "bcd", "def");
System.out.println("colon separated String : "+colonSeparatedValue);

This will print the following String:


colon separated String :abc:bcd:def

Write Custom FunctionalInterface to implement sorting and use in Stream API.
https://beginnersbook.com/2017/10/java-8-lambda-comparator-example-for-sorting-list-of-custom-objects/


explain parallelSort() in the Arrays class of java.util package
Algorithm of parallel sorting:
1. The given array is divided into the sub arrays and the sub arrays are further divided into the their sub arrays, this happens until the sub array reaches a minimum granularity.
2. The sub arrays are sorted individually by multiple threads. The parallel sort uses Fork/Join Framework for sorting sub arrays parallelly.
3. The sorted sub arrays are merged.
Advantage of Parallel Sort Over Simple Sort:
The parallelSort() method uses the concept of multithreading which makes it much faster compared to the normal sort when there are lot of elements.

Example 1: Sorting Primitive Data types with Parallel Sort

import java.util.Arrays; 
public class Example {  
   public static void main(String[] args) {
 int numbers[] = {22, 89, 1, 32, 19, 5};
 //Parallel Sort method for sorting int array
 Arrays.parallelSort(numbers);
 //converting the array to stream and displaying using forEach
 Arrays.stream(numbers).forEach(n->System.out.print(n+" "));
    }
}
Output:
1 5 19 22 32 89

Example 2: Parallel Sort by specifying the start and end index

We can also specify the start and end for the sorting, in this case the sub array starting from the start index and ending at the end index is sorted, the rest of the array is ignored and doesn’t get sorted.
import java.util.Arrays; 
public class Example {  
   public static void main(String[] args) {
 int numbers[] = {22, 89, 1, 32, 19, 5};
 /* Specifying the start and end index. The start index is
  * 1 here and the end index is 5. which means the the elements
  * starting from index 1 till index 5 would be sorted.
  */
 Arrays.parallelSort(numbers, 1, 5);
 //converting the array to stream and displaying using forEach
 Arrays.stream(numbers).forEach(n->System.out.print(n+" "));
   }
}
Output:
22 1 19 32 89 5


Question: explain the Consumer<T> interface?
Answer: generally to write Lambda expression we need to have functionalinterface in place.
What if, i dont have any functional interface written then I can use use Consumer interface.
I will write lambda expression and assign the implementation in Consumer interface and then call its accept() to execute the lambda expression.

https://www.google.com/amp/s/www.geeksforgeeks.org/java-8-consumer-interface-in-java-with-examples/amp/

In JDK8, how many threads are spawned when i'm using parallelStream? For instance, in the code:
list.parallelStream().forEach(/** Do Something */);
If this list has 100000 items, how many threads will be spawned?
Also, do each of the threads get the same number of items to work on or is it randomly allotted?
Answer:
The Oracle's implementation[1] of parallel stream uses the current thread and in addition to that, if needed, also the threads that compose the default fork join pool ForkJoinPool.commonPool(), which has a default size equal to one less than the number of cores of your CPU.
That default size of the common pool can be changed with this property:
-Djava.util.concurrent.ForkJoinPool.common.parallelism=8
Difference between forEach() (Aggregate Operations from stream api) method and Iterator?
1. forEach() Aggregate operations do not contain a method like next to instruct them to process the next element of the collection.
Iterator uses hasNext() and next() function to get the value.

2. forEach() is a method and it is introduced int 1.8 while Iterator is a interface.

3. forEach() Aggregate operations process elements from a stream, not directly from a collection. 
    Iterator process element from the original collections so parallel modification may throw exception.

4. You can specify lambda expressions as parameters for most aggregate operations. This enables you to customize the behavior of a particular aggregate operation.

What is return type of Collectors.toList() method?
As with most operations in the Collectors class, the toList operator returns an instance of Collector, not a collection.

Lets suppose we have List of Person objects, Person will have gender as MALE or FEMALE. now I want to get a map all person based on their gender as MALE and FEMALE. 
For Example: map will have MALE as key and list of Person as value. similarly FEMALE as key and list of Person as value

OR
How to perform grouping in stream api.

The following example groups members of the collection roster by gender:
Map<Person.Gender, List<Person>> byGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(Person::getGender));

The groupingBy operation returns a map whose keys are the values that result from applying the lambda expression specified as its parameter (which is called a classification function). In this example, the returned map contains two keys, Person.Gender.MALE and Person.Gender.FEMALE. The keys' corresponding values are instances of List that contain the stream elements that, when processed by the classification function, correspond to the key value. For example, the value that corresponds to key Person.Gender.MALE is an instance of List that contains all male members.

How to group names of person on their gender from above example

The following example retrieves the names of each member in the collection roster and groups them by gender:
Map<Person.Sex, List<String>> namesByGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(
                Person::getGender,                      
                Collectors.mapping(
                    Person::getName,
                    Collectors.toList())));
The groupingBy operation in this example takes two parameters, a classification function and an instance of Collector. The Collector parameter is called a downstream collector. This is a collector that the Java runtime applies to the results of another collector. Consequently, this groupingBy operation enables you to apply a collect method to the List values created by the groupingBy operator. This example applies the collector mapping, which applies the mapping function Person::getName to each element of the stream. Consequently, the resulting stream consists of only the names of members.

How to retrieves the total age of members of each gender:

Map<Person.Sex, Integer> totalAgeByGender =
    roster
        .stream()
        .collect(
            Collectors.groupingBy(
                Person::getGender,                      
                Collectors.reducing(
                    0,
                    Person::getAge,
                    Integer::sum)));
The reducing operation takes three parameters:
  • identity: Like the Stream.reduce operation, the identity element is both the initial value of the reduction and the default result if there are no elements in the stream. In this example, the identity element is 0; this is the initial value of the sum of ages and the default value if no members exist.
  • mapper: The reducing operation applies this mapper function to all stream elements. In this example, the mapper retrieves the age of each member.
  • operation: The operation function is used to reduce the mapped values. In this example, the operation function adds Integer values.
How retrieves the average age of members of each gender:

Map<Person.Sex, Double> averageAgeByGender = roster
    .stream()
    .collect(
        Collectors.groupingBy(
            Person::getGender,                      
            Collectors.averagingInt(Person::getAge)));


How to get IntStream from stream of Person objects?

IntStream ageStream = roster
    .parallelStream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge);


What is parallel stream in jdk 1.8?

is stream api lazy loaded?
All intermediate operations are lazy. An expression, method, or algorithm is lazy if its value is evaluated only when it is required. (An algorithm is eager if it is evaluated or processed immediately.) Intermediate operations are lazy because they do not start processing the contents of the stream until the terminal operation commences. Processing streams lazily enables the Java compiler and runtime to optimize how they process streams. For example, in a pipeline such as the filter-mapToInt-average example described in the section Aggregate Operations, the average operation could obtain the first several integers from the stream created by the mapToInt operation, which obtains elements from the filter operation. The average operation would repeat this process until it had obtained all required elements from the stream, and then it would calculate the average.

What is lambda expression interference in stream api?

Interference

Lambda expressions in stream operations should not interfere. Interference occurs when the source of a stream is modified while a pipeline processes the stream. For example, the following code attempts to concatenate the strings contained in the List listOfStrings. However, it throws a ConcurrentModificationException:
try {
    List<String> listOfStrings =
        new ArrayList<>(Arrays.asList("one", "two"));
         
    // This will fail as the peek operation will attempt to add the
    // string "three" to the source after the terminal operation has
    // commenced. 
             
    String concatenatedString = listOfStrings
        .stream()
        
        // Don't do this! Interference occurs here.
        .peek(s -> listOfStrings.add("three"))
        
        .reduce((a, b) -> a + " " + b)
        .get();
                 
    System.out.println("Concatenated string: " + concatenatedString);
         
} catch (Exception e) {
    System.out.println("Exception caught: " + e.toString());
}
This example concatenates the strings contained in listOfStrings into an Optional<String> value with the reduce operation, which is a terminal operation. However, the pipeline here invokes the intermediate operation peek, which attempts to add a new element to listOfStrings. Remember, all intermediate operations are lazy. This means that the pipeline in this example begins execution when the operation get is invoked, and ends execution when the get operation completes. The argument of the peek operation attempts to modify the stream source during the execution of the pipeline, which causes the Java runtime to throw a ConcurrentModificationException.

What is stateful lambda expression?
Avoid using stateful lambda expressions as parameters in stream operations. A stateful lambda expression is one whose result depends on any state that might change during the execution of a pipeline. The following example adds elements from the List listOfIntegers to a new List instance with the map intermediate operation. It does this twice, first with a serial stream and then with a parallel stream:
List<Integer> serialStorage = new ArrayList<>();
     
System.out.println("Serial stream:");
listOfIntegers
    .stream()
    
    // Don't do this! It uses a stateful lambda expression.
    .map(e -> { serialStorage.add(e); return e; })
    
    .forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");
     
serialStorage
    .stream()
    .forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");

System.out.println("Parallel stream:");
List<Integer> parallelStorage = Collections.synchronizedList(
    new ArrayList<>());
listOfIntegers
    .parallelStream()
    
    // Don't do this! It uses a stateful lambda expression.
    .map(e -> { parallelStorage.add(e); return e; })
    
    .forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");
     
parallelStorage
    .stream()
    .forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");
The lambda expression e -> { parallelStorage.add(e); return e; } is a stateful lambda expression. Its result can vary every time the code is run. This example prints the following:
Serial stream:
8 7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
Parallel stream:
8 7 6 5 4 3 2 1
1 3 6 2 4 5 8 7
The operation forEachOrdered processes elements in the order specified by the stream, regardless of whether the stream is executed in serial or parallel. However, when a stream is executed in parallel, the map operation processes elements of the stream specified by the Java runtime and compiler. Consequently, the order in which the lambda expression e -> { parallelStorage.add(e); return e; } adds elements to the List parallelStorage can vary every time the code is run. For deterministic and predictable results, ensure that lambda expression parameters in stream operations are not stateful.
Note: This example invokes the method synchronizedList so that the List parallelStorage is thread-safe. Remember that collections are not thread-safe. This means that multiple threads should not access a particular collection at the same time. Suppose that you do not invoke the method synchronizedList when creating parallelStorage:
List<Integer> parallelStorage = new ArrayList<>();
The example behaves erratically because multiple threads access and modify parallelStorage without a mechanism like synchronization to schedule when a particular thread may access the List instance. Consequently, the example could print output similar to the following:
Parallel stream:
8 7 6 5 4 3 2 1
null 3 5 4 7 8 1 2



References
https://beginnersbook.com/2017/10/java-8-arrays-parallel-sort-with-example/
SHARE

esant technology

0 comments :

Post a Comment