Java Interview QuestionsNo Comments

Functional Programming with Lambda Expressions and Streams

1. What is functional programming?

Functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.


2. Can you give an example of functional programming?

@Test 
public void sumOfOddNumbers_Usual() {
    List < Integer > numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
    int sum = 0;
    for (int number: numbers)
        if (number % 2 != 0) sum += number;
    assertEquals(11, sum);
}

@Test 
public void sumOfOddNumbers_FunctionalProgrammingExample() {
    List < Integer > numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
    int sum = numbers.stream().filter(Test123::isOdd).reduce(0, Integer::sum);
    assertEquals(11, sum);
}

static boolean isOdd(int number) {
    return number % 2 != 0;
}

3. What is a Stream?

A Stream is a source of objects. In the above example, we created a stream from List.
Streams have Intermediate Operations and Terminal Operations. In the example above, we used filter as intermediate operation and reduce as a terminal operation.


4. Explain about streams with an example?

Streams are introduced in Java 8. In combination with Lambda expressions, they attempt to bring some of the important functional programming concepts to Java.

A stream is a sequence of elements supporting sequential and parallel aggregate operations. Consider the example code below. Following steps are done:-

• Step I : Creating an array as a stream

• Step II : Use Lambda Expression to create a filter

• Step III : Use map function to invoke a String function

• Step IV : Use sorted function to sort the array

• Step V : Print the array using forEach

Arrays.stream(new String[] {
    "Ram",
    "Robert",
    "Rahim"
})
    .filter(s - > s.startsWith("Ro"))
    .map(String::toLowerCase)
    .sorted()
    .forEach(System.out::println);

In general any use of streams involves :-

• Source – Creation or use of existing stream : Step I above

• Intermediate Operations – Step II, III and IV above. Intermediate Operations return a new stream

• Terminal Operation – Step V. Consume the stream. Print it to output or produce a result (sum,min,max etc).

Intermediate Operations are of two kinds :-

• Stateful : Elements need to be compared against one another (sort, distinct etc)

• Stateless : No need for comparing with other elements (map, filter etc)


5. What are Intermediate Operations in Streams?

An Intermediate Operation on a Stream returns another Stream.
Examples : map, filter, distinct, sorted.

Distinct Example

@Test 
public void streamExample_Distinct() {
    List < Integer > numbers = Arrays.asList(1, 1, 2, 6, 2, 3);
    numbers.stream().distinct().forEach(System.out::print);
    // 1263		
}


Sorted Example

@Test 
public void streamExample_Sorted() {
    List < Integer > numbers = Arrays.asList(1, 1, 2, 6, 2, 3);
    numbers.stream().sorted().forEach(System.out::print);
    // 112236		
}


Filter Example

@Test 
public void streamExample_Filter() {
    List < Integer > numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
    numbers.stream().filter(Test123::isOdd).forEach(System.out::print);
    // 137	
}

6. What are Terminal Operations in Streams?

Terminal Operation either produce a result or create a side effect.

reduce is used to cumulate elements.

@Test 
public void sumOfOddNumbers_FunctionalProgramming() {
    List < Integer > numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
    int sum = numbers.stream().filter(Test123::isOdd).reduce(0, Integer::sum);
    assertEquals(11, sum);
}

forEach is used to create a side effect. Print to Output. Store to database.

@Test 
public void streamExample_Filter() {
    List < Integer > numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
    numbers.stream().filter(Test123::isOdd).forEach(System.out::print); 
    // 137		
}

collect is used to group elements to a collection

@Test 
public void streamExample_Collect() {
    List < Integer > numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
    List < Integer > oddNumbers = numbers.stream().filter(Test123::isOdd).collect(Collectors.toList());
    System.out.println(oddNumbers);
    // [1, 3, 7]		
}

7. What are Method References?

Integer::sum, System.out::print in the above examples are method references. These two are simple static methods which are used instead of Lambda Expressions.


8. What are Lambda Expressions?

A lambda expression is an anonymous function. Simply put, it’s a method without a declaration. There will be no access modifiers, no return value declaration, and no name. It’s a shorthand that allows you to write a method in the same place you are going to use it. Especially useful in places where a method is being used only once, and the method definition is short.

Syntax : Parameters -> Executed code


8. Can you give an example of Lambda Expression?

In the example below, number -> System.out.print(number) is a lambda expression.

@Test 
public void lambdaExpression_simpleExample() {
    List < Integer > numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
    numbers.stream().filter(Test123::isOdd).forEach(number - > System.out.print(number));
    // 137	
}

9. Can you explain the relationship between Lambda Expression and Functional Interfaces?

Look at the earlier example : Function we passed in is number -> System.out.print(number). Input to this function is number. The function consumes it and prints it to the output.

forEach function has an interface – void java.util.stream.Stream.forEach(Consumer action)

The JavaDoc for java.util.function.Consumer reads – @FunctionalInterface : Represents an operation that accepts a single input argument and returns no result. Unlike most other functional interfaces, Consumer is expected to operate via side-effects.

When ever we create a Lambda Expression, we are defining a function which implements a predefined/custom defined Functional Interface.


10. What is a Predicate?

@FunctionalInterface 
public interface Predicate < T > {
    boolean test(T t);
}
@Test 
public void lambdaExpression_predicate() {
    List < Integer > numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
    numbers.stream()
    .filter((number) - > (number % 2 != 0))
    .forEach(number - > System.out.print(number));
    // 137	
}

(number) -> (number % 2 != 0) is a Predicate. Takes an argument and returns true of false.

Signature of filter function : Stream java.util.stream.Stream.filter(Predicate predicate). filter returns a stream consisting of the elements of this stream that match the given predicate.


11. What is the functional interface – Function?

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

12. What is a Consumer?

@FunctionalInterface
public interface Consumer < T > {
    void accept(T t);
}

12. Can you give examples of functional interfaces with multiple arguments?

@FunctionalInterface
public interface BiFunction < T, U, R > {
    R apply(T t, U u);
}

13. What are the new features in Java 7?

• Diamond Operator.

o Example :

Map<String , List <Trade>> trades = new TreeMap <> ();

• Using String in switch statements

• Automatic resource management

o try(resources_to_be_closed){ // your code }

• Numeric literals with underscores

• Improved exception handling

o Multiple catches in same block

o catch(ExceptionOne | ExceptionTwo | ExceptionThree e)


14. What are the new features in Java 8?

Java 8 brought in a number of important new features.

• Lamda Expressions. Example : Runnable java8Runner = () -> { Sysout(“I am running”); };

• Nashorn : javascript engine that enables us to run javascript to run on a jvm

• String.join() function

• Streams