Java 8 Functional Interfaces – When & How To Use Them?

Functional interfaces, lambda expressions and Stream API – these three features of Java 8 has turned Java programming into new style of programming called functional-style programming. Java is still an object-oriented programming language, but from Java 8, with the introduction of new features, most of the programming is done keeping functions in mind rather than objects. In this article, we will see Java 8 functional interfaces, @FunctionalInterface annotation, java.util.function package and how to use new Java 8 functional interfaces to compose lambda expressions with some simple examples.

Java 8 Functional Interfaces

1) Definition

Functional interfaces are the interfaces which has exactly one abstract method. They may have any number of default methods but must have only one abstract method. Functional interfaces provide only one functionality to implement.

There were functional interfaces exist before Java 8. It is not like that they are the whole new concept introduced only in Java 8. RunnableActionListenerCallable and Comaprator are some old functional interfaces which exist even before Java 8.

The new set of functional interfaces are introduced in Java 8 to make programmer’s job easy while writing lambda expressions. Your lambda expression must implement any one of these functional interfaces. These new functional interfaces are organised under java.util.function package.

2) @FunctionalInterface Annotation

@FunctionalInterface annotation is introduced in Java 8 to represent functional interfaces. Although, it is not compulsory to write functional interface using this annotation. But, if you are using @FunctionalInterface annotation then your interface should contain only one abstract method. If you try to write more than one abstract method, compiler will show the error.

3) java.util.function package

All Java 8 functional interfaces are organised in java.util.function package. Each functional interface in this package represents an operation that can be performed by the lambda expression.

Below table shows the list of all Java 8 functional interfaces along with their abstract method, which operation they represent and when to use them?

Java 8 functional interfaces

4) How To Use Java 8 Functional Interfaces In Real Time?

Let’s define Student class like below. We will be using this class in the subsequent examples.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Student
{
    int id;
    
    String name;
    
    double percentage;
    
    String specialization;
    
    public Student(int id, String name, double percentage, String specialization)
    {
        this.id = id;
        
        this.name = name;
        
        this.percentage = percentage;
        
        this.specialization = specialization;
    }
    
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public double getPercentage() {
        return percentage;
    }
    public String getSpecialization() {
        return specialization;
    }
    @Override
    public String toString()
    {
        return id+"-"+name+"-"+percentage+"-"+specialization;
    }
}

Let listOfStudents be the list of 10 students.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
List<Student> listOfStudents = new ArrayList<Student>();
        
listOfStudents.add(new Student(111, "John", 81.0, "Mathematics"));
        
listOfStudents.add(new Student(222, "Harsha", 79.5, "History"));
        
listOfStudents.add(new Student(333, "Ruth", 87.2, "Computers"));
        
listOfStudents.add(new Student(444, "Aroma", 63.2, "Mathematics"));
        
listOfStudents.add(new Student(555, "Zade", 83.5, "Computers"));
        
listOfStudents.add(new Student(666, "Xing", 58.5, "Geography"));
        
listOfStudents.add(new Student(777, "Richards", 72.6, "Banking"));
        
listOfStudents.add(new Student(888, "Sunil", 86.7, "History"));
        
listOfStudents.add(new Student(999, "Jordan", 58.6, "Finance"));
        
listOfStudents.add(new Student(101010, "Chris", 89.8, "Computers"));

Let’s see how to use 4 important functional interfaces – PredicateConsumerFunction and Supplier using above listOfStudents.

a) Predicate – Tests an object

Predicate represents an operation which takes an argument T and returns a boolean. Use this functional interface, if you want to define a lambda expression which performs some test on an argument and returns true or false depending upon outcome of the test.

For example,

Imagine an operation where you want only a list of “Mathematics” students from the above listOfStudents. Let’s see how to do it using Predicate.

Lambda expression implementing Predicate : Checking specialization of a Student

1
2
3
4
5
6
7
8
9
10
11
Predicate<Student> mathematicsPredicate = (Student student) -> student.getSpecialization().equals("Mathematics");
        
List<Student> mathematicsStudents = new ArrayList<Student>();
        
for (Student student : listOfStudents)
{
    if (mathematicsPredicate.test(student))
    {
        mathematicsStudents.add(student);
    }
}

b) Consumer – Consumes an object

Consumer represents an operation which takes an argument and returns nothing. Use this functional interface If you want to compose a lambda expression which performs some operations on an object.

For example, displaying all students with their percentage.

Lambda expression implementing Consumer : Displaying all students with their percentage

1
2
3
4
5
6
7
8
Consumer<Student> percentageConsumer = (Student student) -> {
        System.out.println(student.getName()+" : "+student.getPercentage());
    };
        
for (Student student : listOfStudents)
{
    percentageConsumer.accept(student);
}

c) Function – Applies to an object

Function represents an operation which takes an argument of type T and returns a result of type R. Use this functional interface if you want to extract some data from an existing data.

For example, extracting only the names from listOfStudents.

Lambda expression implementing Function : Extracting only the names of all students

1
2
3
4
5
6
7
8
Function<Student, String> nameFunction = (Student Student) -> Student.getName();
        
List<String> studentNames = new ArrayList<String>();
        
for (Student student : listOfStudents)
{
    studentNames.add(nameFunction.apply(student));
}

d) Supplier – Supplies the objects

Supplier represents an operation which takes no argument and returns the results of type R. Use this functional interface when you want to create new objects.

Lambda expression implementing Supplier : Creating a new Student

1
2
3
Supplier<Student> studentSupplier = () -> new Student(111111, "New Student", 92.9, "Java 8");
        
listOfStudents.add(studentSupplier.get());

5) Functional Interfaces Supporting Primitive Type

Java 8 has also introduced functional interfaces which support primitive types. For example IntPredicateDoublePredicateLongConsumer etc… (See above table).

If an input or output is a primitive type then using these functional interfaces will enhance the performance of your code. For example, if input to a Predicate is primitive type intthen using intPredicate instead of Predicate will remove unnecessary boxing of input.

Functional Programming Paradigm

Introduction
Functional programming is a programming paradigm in which we try to bind everything in pure mathematical functions style. It is a declarative type of programming style. Its main focus is on “what to solve” in contrast to an imperative style where the main focus is “how to solve”. It uses expressions instead of statements. An expression is evaluated to produce a value whereas a statement is executed to assign variables. Those functions have some special features discussed below.

Functional Programming is based on Lambda Calculus:
Lambda calculus is framework developed by Alonzo Church to study computations with functions. It can be called as the smallest programming language of the world. It gives the definition of what is computable. Anything that can be computed by lambda calculus is computable. It is equivalent to Turing machine in its ability to compute. It provides a theoretical framework for describing functions and their evaluation. It forms the basis of almost all current functional programming languages.
Fact: Alan Turing was a student of Alonzo Church who created Turing machine which laid the foundation of imperative programming style.

Programming Languages that support functional programming: Haskell, JavaScript, Scala, Erlang, Lisp, ML, Clojure, OCaml, Common Lisp, Racket.

Concepts of functional programming:

  • Pure functions
  • Recursion
  • Referential transparency
  • Functions are First-Class and can be Higher-Order
  • Variables are Immutable

Pure functions: These functions have two main properties. First, they always produce the same output for same arguments irrespective of anything else.
Secondly, they have no side-effects i.e. they do modify any argument or global variables or output something.
Later property is called immutability. The pure functions only result is the value it returns. They are deterministic.
Programs done using functional programming are easy to debug because pure functions have no side effect or hidden I/O. Pure functions also make it easier to write parallel/concurrent applications. When the code is written in this style, a smart compiler can do many things – it can parallelize the instructions, wait to evaluate results when need them, and memorize the results since the results never change as long as the input doesn’t change.
example of the pure function:

sum(x, y)           // sum is function taking x and y as arguments
    return x + y    // sum is returning sum of x and y without changing them

Recursion: There are no “for” or “while” loop in functional languages. Iteration in functional languages is implemented through recursion. Recursive functions repeatedly call themselves, until it reaches the base case.
example of the recursive function:

fib(n)
    if (n <= 1)
        return 1;
    else
        return fib(n - 1) + fib(n - 2);

Referential transparency: In functional programs variables once defined do not change their value throughout the program. Functional programs do not have assignment statements. If we have to store some value, we define new variables instead. This eliminates any chances of side effects because any variable can be replaced with its actual value at any point of execution. State of any variable is constant at any instant.
example:

x = x + 1 // this changes the value assigned to the variable x.
          // So the expression is not referentially transparent. 

Functions are First-Class and can be Higher-Order: First-class functions are treated as first-class variable. The first class variables can be passed to functions as parameter, can be returned from functions or stored in data structures. Higher order functions are the functions that take other functions as arguments and they can also return functions.
example:

show_output(f)            // function show_output is declared taking argument f 
                          // which are another function
    f();                  // calling passed function

print_gfg()             // declaring another function 
    print("hello gfg");

show_output(print_gfg)  // passing function in another function

Variables are Immutable: In functional programming, we can’t modify a variable after it’s been initialized. We can create new variables – but we can’t modify existing variables, and this really helps to maintain state throughout the runtime of a program. Once we create a variable and set its value, we can have full confidence knowing that the value of that variable will never change.

Advantages and Disadvantages of Functional programming

Advantages:

  1. Pure functions are easier to understand because they don’t change any states and depend only on the input given to them. Whatever output they produce is the return value they give. Their function signature gives all the information about them i.e. their return type and their arguments.
  2. The ability of functional programming languages to treat functions as values and pass them to functions as parameters make the code more readable and easily understandable.
  3. Testing and debugging is easier. Since pure functions take only arguments and produce output, they don’t produce any changes don’t take input or produce some hidden output. They use immutable values, so it becomes easier to check some problems in programs written uses pure functions.
  4. It is used to implement concurrency/parallelism because pure functions don’t change variables or any other data outside of it.
  5. It adopts lazy evaluation which avoids repeated evaluation because the value is evaluated and stored only when it is needed.

Disadvantages:

  1. Sometimes writing pure functions can reduce the readability of code.
  2. Writing programs in recursive style instead of using loops can be bit intimidating.
  3. Writing pure functions are easy but combining them with rest of application and I/O operations is the difficult task.
  4. Immutable values and recursion can lead to decrease in performance.

Applications:

  • It is used in mathematical computations.
  • It is needed where concurrency or parallelism is required.

Fact: Whatsapp needs only 50 engineers for its 900M user because Erlang is used to implement its concurrency needs. Facebook uses Haskell in its anti-spam system.

Java Garbage Collection Basics

Objects dynamically created using a new operator are deallocated automatically. The technique that accomplishes this is called garbage collection. It works like this: When no references to an object exist, that object is assumed to be no longer needed, and the memory occupied by the object can be reclaimed.

How Does Automatic Garbage Collection Work?

Automatic garbage collection works by looking at heap memory, identifying which objects are being referenced and which are not, and deleting the unused objects. This is widely known as the “Mark and Sweep Algorithm.”

The process of deallocating memory is handled automatically by the garbage collector in two steps:

Step 1: Marking

This is first step where the garbage collector identifies which pieces of memory are in use and which are not and marks those objects that are not referenced anymore.

Garbage Collection Marking

Source: Oracle Java Docs

Step 2: Deleting

In this step, all the marked objects, which are not referenced anymore, in Step 1 are deleted and reclaim the memory.

This can be done in two types:

2a) Normal Deletion

In this deletion, the memory allocator holds references to blocks of free space where a new object can be allocated, as shown in figure below.

Garbage Collection-Normal Deletion

Source: Oracle Java Docs

2b) Deletion With Compacting

In this process, instead of just removing objects from memory, the remaining objects will be compacted to further improve performance, in addition to deleting unreferenced objects. By moving referenced object together, this makes new memory allocation much easier and faster.

Garbage Collection- Compact Deletion

Source: Oracle Java Docs

For further information on how garbage collection works, please refer to this Oracle Java Docs.

One Method to Rule Them All: Map.merge()

 I don’t often explain a single method in JDK, but when I do, it’s about Map.merge(). This is probably the most versatile operation in the key-value universe. But it’s also rather obscure and rarely used.

merge() can be explained as follows: it either puts new value under the given key (if absent) or updates existing key with a given value (UPSERT). Let’s start with the most basic example: counting unique word occurrences. Pre-Java 8 (read: pre-2014!) code was quite messy and the essence was lost in implementation details:

var map = new HashMap<String, Integer>();
words.forEach(word -> {
    var prev = map.get(word);
    if (prev == null) {
        map.put(word, 1);
    } else {
        map.put(word, prev + 1);
    }
});

However, it works, and for the given input, it produces the desired output:

var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz");
//...
{Bar=1, Fizz=2, Foo=3, Buzz=2}

OK, but let’s try to refactor it to avoid conditional logic:

words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.put(word, map.get(word) + 1);
});

That’s nice!

putIfAbsent()  is a necessary evil; otherwise, the code breaks on the first occurrence of a previously unknown word. Also, I find map.get(word) insidemap.put() to be a bit awkward. Let’s get rid of it as well!

words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.computeIfPresent(word, (w, prev) -> prev + 1);
});

computeIfPresent() invokes the given transformation only if the key in question (word) exists. Otherwise, it does nothing. We made sure the key exists by initializing it to zero, so incrementation always works. Can we do better? Well, we can cut the extra initialization, but I wouldn’t recommend it:

words.forEach(word ->
        map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1)
);

compute ()is likecomputeIfPresent(), but it is invoked irrespective to the existence of the given key. If the value for the key does not exist, the prevargument is null. Moving a simpleif to a ternary expression hidden in lambda is far from optimal. This is where themerge()operator shines. Before I show you the final version, let’s see a slightly simplified default implementation ofMap.merge():

default V merge(K key, V value, BiFunction<V, V, V> remappingFunction) {
    V oldValue = get(key);
    V newValue = (oldValue == null) ? value :
               remappingFunction.apply(oldValue, value);
    if (newValue == null) {
        remove(key);
    } else {
        put(key, newValue);
    }
    return newValue;
}

The code snippet is worth a thousand words. merge() works in two scenarios. If the given key is not present, it simply becomes put(key, value). However, if the said key already holds some value, our remappingFunction may merge (duh!) the old and the one. This function is free to:

  • overwrite old value by simply returning the new one: (old, new) -> new
  • keep the old value by simply returning the old one: (old, new) -> old
  • somehow merge the two, e.g.: (old, new) -> old + new
  • or even remove old value: (old, new) -> null

As you can see, merge() is quite versatile. So, how does our academic problem look like with merge()? It’s quite pleasing:

words.forEach(word ->
        map.merge(word, 1, (prev, one) -> prev + one)
);

You can read it as follows: put 1 under the word key if absent; otherwise, add 1 to the existing value. I named one of the parameters “ one” because in our example it’s always…  1.

Sadly, remappingFunctiontakes two parameters, where the second one is the value we are about to upsert (insert or update). Technically, we know this value already, so (word, 1, prev -> prev + 1)  would be much easier to digest. But there’s no such API.

All right, but is merge() really useful? Imagine you have an account operation (constructor, getters, and other useful properties omitted):

class Operation {
    private final String accNo;
    private final BigDecimal amount;
}

And a bunch of operations for different accounts:

var operations = List.of(
    new Operation("123", new BigDecimal("10")),
    new Operation("456", new BigDecimal("1200")),
    new Operation("123", new BigDecimal("-4")),
    new Operation("123", new BigDecimal("8")),
    new Operation("456", new BigDecimal("800")),
    new Operation("456", new BigDecimal("-1500")),
    new Operation("123", new BigDecimal("2")),
    new Operation("123", new BigDecimal("-6.5")),
    new Operation("456", new BigDecimal("-600"))
);

We would like to compute balance (total over operations’ amounts) for each account. Without merge(), this is quite cumbersome:

var balances = new HashMap<String, BigDecimal>();
operations.forEach(op -> {
    var key = op.getAccNo();
    balances.putIfAbsent(key, BigDecimal.ZERO);
    balances.computeIfPresent(key, (accNo, prev) -> prev.add(op.getAmount()));
});

But with a little help of merge():

operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), 
                (soFar, amount) -> soFar.add(amount))
);

Do you see a method reference opportunity here?

operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add)
);

I find this astoundingly readable. For each operation, add the given amount to the given accNo. The results are as expected:

{123=9.5, 456=-100}

ConcurrentHashMap

Map.merge() shines even brighter when you realize it’s properly implemented inConcurrentHashMap. This means we can atomically perform an insert-or-update operation — single line and thread-safe.

ConcurrentHashMap is obviously thread-safe, but not across many operations, e.g. get() and thenput(). However, merge() makes sure no updates are lost.

Life Beyond Java 8

New versions of Java are coming out every six months. What has changed, should we upgrade, and if so, how?

Abstract

Wasn’t Java 8 a fantastic update to the language? Lambdas and streams were a huge change and have helped to improve Java developers’ productivity and introduce some functional ideas to the language. Then came Java 9… and although the module system is really interesting for certain types of applications, the lack of exciting language features and uncertainty around how painful it might be to migrate to Java 9 left many applications taking a wait-and-see approach, happy with Java 8.

But now, Java has a new version every six months, and suddenly, Java 12 is here. But we’re all still on Java 8, wondering whether we should move to a later version, which one to choose, and how painful it might be to upgrade.

In this session, we’ll look at:

  • Why upgrade from Java 8, including language features from Java 9, 10, 11, and 12
  • What sorts of issues might we run into if we do choose to upgrade
  • How the support and license changes that came in with Java 11 might impact us.

Resources

Updates, Licenses, and Support

Where to Get Your JDK From

Migrating From Java 8

Features

Java 11

Java 10

Java 9

Java 12

Java Future

Performance

Garbage Collectors

String Performance

Other

50+ Java Interview Questions for Programmers

Hello, guys! Recently, I have been sharing a lot of Java Interview questions and discussion individually, and many of my readers requested to bring them together so that they can have them in the same page and prepare better and this post is the result of that.

This article contains more than 50 Java Interview questions covering all important topics like core Java fundamentals, Java Collection FrameworkJava Multithreading and ConcurrencyJava IOJDBCJVM InternalsCoding ProblemsObject-Oriented programming, etc.

The questions are also picked up from various interviews and they are, by no means, very difficult, and you might have seen them already in your telephonic or face-to-face round of interview.

The questions are also very good to revise important topics like multithreading and collections as I have also shared some useful resources for further learning and improvement like The Complete Java MasterClass to brush up and fill gaps in your Java skills.

So what are we waiting for here is the list of some of the frequently asked Java questions from interviews from both beginner and experienced Java developer of 2 to 5 years experience:

Java Interview Questions and Answers

1)  How Java achieves platform independence? (answer)
hint: bytecode and Java Virtual Machine

2)  What is ClassLoader in Java? (answer)
hint: part of JVM that loads bytecodes for classes. You can write your own.

3)  Write a Java program to check if a number is Even or Odd? (answer)
hint: you can use bitwise operator, e.g. bitwise AND &, remember, even the number has zero at the end in binary format and an odd number has 1 in the end.

4)  Difference between ArrayList and HashSet in Java? (answer)
hint: all differences between List and Set are applicable here, e.g. ordering, duplicates, random search, etc.

5)  What is double checked locking in Singleton? (answer)
hint: two-time check whether instances is initialized or not, first without locking and second with locking.

6)  How do you create thread-safe Singleton in Java? (answer)
hint: many ways, e.g. using Enum or by using double-checked locking pattern or using a nested static class.

7)  When to use volatile variable in Java? (answer)
hint: when you need to instruct the JVM that a variable can be modified by multiple threads and give hint to JVM that does not cache its value.

8)  When to use a transient variable in Java? (answer)
hint: when you want to make a variable non-serializable in a class, which implements the Serializable interface. In other words, you can use it for a variable whose value you don’t want to save. See The Complete Java MasterClass to learn about transient variables in Java.

9)  Difference between the transient and volatile variable in Java? (answer)
hint: totally different, one used in the context of serialization while the other is used in concurrency.

10) Difference between Serializable and Externalizable in Java? (answer)
hint: Externalizable gives you more control over the Serialization process.

11) Can we override the private method in Java? (answer)
hint: No, because it’s not visible in the subclass, a primary requirement for overriding a method in Java.

12) Difference between Hashtable and HashMap in Java? (answer)

hint: several but most important is Hashtable, which is synchronized, while HashMap is not. It’s also legacy and slow as compared to HashMap.

13) Difference between Listand Set in Java? (answer)
hint: List is ordered and allows duplicate. Set is unordered and doesn’t allow duplicate elements.

14) Difference between ArrayList and Vector in Java (answer)
hint: Many, but most important is that ArrayList is non-synchronized and fast while Vector is synchronized and slow. It’s also legacy class like Hashtable.

15) Difference between Hashtable and ConcurrentHashMap in Java? (answer)
hint: more scalable

16) How does ConcurrentHashMap achieve scalability? (answer)
hint: by dividing the map into segments and only locking during the write operation.

17) Which two methods you will override for an Object to be used as Key in HashMap? (answer)
hint: equals and hashcode

18) Difference between wait and sleep in Java? (answer)
hint: The wait() method releases the lock or monitor, while sleep doesn’t.

19) Difference between notify and notifyAll in Java? (answer)
hint: notify notifies one random thread is waiting for that lock while notifyAll inform to all threads waiting for a monitor. If you are certain that only one thread is waiting then use notify, or else notifyAll is better. See Threading Essentials Mini-Course by Java Champion Heinz Kabutz to learn more about threading basics.

20) Why you override hashcode, along with equals() in Java? (answer)
hint: to be compliant with equals and hashcode contract, which is required if you are planning to store your object into collection classes, e.g. HashMap or ArrayList.

21) What is the load factor of HashMap means? (answer)
hint: The threshold that triggers the re-sizing of HashMap is generally 0.75, which means HashMap resize itself if it’s 75 percent full.

22) Difference between ArrayList and LinkedList in Java? (answer)
hint: same as an array and linked list, one allows random search while other doesn’t. Insertion and deletion easy on the linked list but a search is easy on an array. See Java Fundamentals: Collections Richard Warburton course on Pluralsight to learn more about essential Collection data structure in Java.

23) Difference between CountDownLatch and CyclicBarrier in Java? (answer)
hint: You can reuse CyclicBarrier after the barrier is broken but you cannot reuse CountDownLatch   after the count reaches to zero.

24) When do you use Runnable vs Thread in Java? (answer)
hint: always

25) What is the meaning of Enum being type-safe in Java? (answer)
hint: It means you cannot assign an instance of different Enum type to an Enum variable. e.g. if you have a variable like DayOfWeek day then you cannot assign it value from DayOfMonth enum.

26) How does Autoboxing of Integer work in Java? (answer)
hint: using valueOf() method

27) Difference between PATH and Classpath in Java? (answer)
hint: PATH is used by the operating system while Classpath is used by JVM to locate Java binary, e.g. JAR files or Class files. See Java Fundamentals: The Core Platform to learn more about PATHClasspath, and other Java environment variable.

28) Difference between method overloading and overriding in Java? (answer)
hint: Overriding happens at subclass while overloading happens in the same class. Also, overriding is a runtime activity while overloading is resolved at compile time.

29) How do you prevent a class from being sub-classed in Java? (answer)
hint: just make its constructor private

30) How do you restrict your class from being used by your client? (answer)
hint:  make the constructor private or throw an exception from the constructor

31) Difference between StringBuilder and StringBuffer in Java? (answer)
hint: StringBuilder is not synchronized while StringBuffer is synchronized.

32) Difference between Polymorphism and Inheritance in Java? (answer)
hint: Inheritance allows code reuse and builds the relationship between class, which is required by Polymorphism, which provides dynamic behavior. See Java Fundamentals: Object-Oriented Design to learn more about OOP features.

33) Can we override static method in Java? (answer)
hint: No, because overriding resolves at runtime while static method call is resolved at compile time.

34) Can we access the private method in Java? (answer)
hint: yes, in the same class but not outside the class

35) Difference between interface and abstract class in Java? (answer)
hint: from Java 8, the difference is blurred. However, a Java class can still implement multiple interfaces but can only extend one class.

36) Difference between DOM and SAX parser in Java? (answer)
hint: DOM loads whole XML File in memory while SAX doesn’t. It is an event-based parser and can be used to parse a large file, but DOM is fast and should be preferred for small files.

37) Difference between throw and throws keyword in Java? (answer)
hint: throws declare what exception a method can throw in case of error but throw keyword actually throws an exception. See Java Fundamentals: Exception Handling to learn more about Exception handling in Java.

38) Difference between fail-safe and fail-fast iterators in Java? (answer)
hint: fail-safe doesn’t throw ConcurrentModificationException while fail-fast does whenever they detect an outside change on the underlying collection while iterating over it.

39) Difference between Iterator and Enumeration in Java? (answer)
hint: Iterator also gives you the ability to remove an element while iterating while Enumeration doesn’t allow that.

40) What is IdentityHashMap in Java? (answer)
hint: A Map, which uses  the == equality operator to check equality instead of the equals() method.

41) What is String pool in Java? (answer)
hint: A pool of String literals. Remember it’s moved to heap from perm gen space in JDK 7.

42) Can a Serializable class contain a non-serializable field in Java? (answer)

hint: Yes, but you need to make it either static or transient.

43) Difference between this and super in Java? (answer)
hint: this refers to the current instance while super refers to an instance of the superclass.

44) Difference between Comparator and Comparable in Java? (answer)
hint: Comparator defines custom ordering while Comparable defines the natural order of objects, e.g. the alphabetic order for String. See The Complete Java MasterClass to learn more about sorting in Java.

45) Difference between java.util.Date and java.sql.Date in Java? (answer)
hint: former contains both date and time while later contains only date part.

46) Why wait and notify method are declared in Object class in Java? (answer)
hint: because they require lock which is only available to an object.

47) Why Java doesn’t support multiple inheritances? (answer)
hint: It doesn’t support because of bad experience with C++, but with Java 8, it does in some sense — only multiple inheritances of Type are not supported in Java now.

48) Difference between checked and unchecked Exception in Java? (answer)
hint: In case of checked, you must handle exception using catch block, while in case of unchecked, it’s up to you; compile will not bother you.

49) Difference between Error and Exception in Java? (answer)
hint: I am tired of typing please check the answer

50) Difference between race condition and deadlock in Java? (answer)
hint: both are errors that occur in a concurrent application, one occurs because of thread scheduling while others occur because of poor coding.

Additional Resources

Java 8: A quick introduction to Parallelism and the Spliterator

java-8-parellelism-spliterator

With the release of Java 8 a number of new language features were introduced [1]. These included lambda functionsstreamsand completable futures. Colleagues of mine have already reviewed these features in previous articles on this blog, which I recommend reading as part of this topic [2][3]. In this article I will touch on an aspect of the Java 8 release that relates to the push towards exploiting parallelism, in the context of the existing Collections Framework – specifically the new Spliterator interface.

Due to the size and complexity of these topics, it will only be possible to provide a high-level introduction, but it is hoped that this will trigger interest in the reader, to go off and delve further into an area of growing importance and interest.

Parallelism

First thing first – what is meant by the term ‘parallelism’? For the purpose of this article, at a high level, it is a term that refers to programming in a way to exploit multi-core CPU units. That is to say, to take a piece of work (a task) and break it out into separate units (sub-tasks) that can be processed in parallel. Then aggregating the results of all the processed units to complete the original work [4]. This is sufficient for now, although the reader might want to investigate further on differences between parallelism, concurrency and sequential programming.

Figure 1

For this to be successful, the processing on a unit should not have a dependency on any other unit – this processing should be stateless and also should not depend on the result of the processing of any other unit. This is important when it comes to the data that is involved – as parallel computing lends itself to situations where vast amounts of data is to be handled.

This also raises the question of when to go for parallelism – there is an overhead in the added complexity of identifying and breaking out the units of work and then coordinating the parallel processing. Then there is also the issue of latency in reading the data – the savings found in using parallel cores for processing is to try to keep each CPU core busy all the time – which requires reading data as and when is needed without delay. Before embarking on a parallel processing architecture, some cost-benefit analysis is required to be sure that this is the right approach.

With the introduction of Java 8 Oracle was aiming to make it more attractive and simpler for developers to use Java in parallel programming. The inclusion of features such as lambda functions and the Streams API was to be seen as part of this push [5]. How successful that becomes, time will tell.

This concept of parallelism is behind the existing fork/join framework found in Java, which will be covered in a future article, but for now we will confine our overview to the new Spliteratorinterface, found in the java.util library.

Collections Framework

The Collections Framework is also found in the java.util library, and provides a range of classes that act as containers to hold objects (technically, references to the objects) and allow useful behaviour such as adding new objects, searching the container for a particular object, and the sorting of such objects – the behaviour available is depending on the type of container used [6].

Within the library there are two different concepts of containers, captured in the core interfaces, that are defined within the framework [7]. One concept is that of a Collection that is a sequence of individual elements, which is further refined by extending the Collectioninterface with interfaces for ListSetQueue or Deque.

Listing 1 – Simple Iterator on an ArrayList

// Create a new Collection type; in this case an ArrayList Collection<Person> people = new ArrayList<Person>(); // Add some people using Person objects people.add(new Person("Jane", "Doe", "Ireland")); people.add(new Person("Joe", "Doe", "England")); people.add(new Person("John", "Doe", "Scotland")); people.add(new Person("Julie", "Doe", "Wales")); people.add(new Person("Jerry", "Doe", "France")); people.add(new Person("Jim", "Doe", "Italy")); // simple iterator example Iterator<Person> peopleIterator = people.iterator(); while(peopleIterator.hasNext()) { Person person = peopleIterator.next(); System.out.println("Hello " + person.getFirst_name() + " " + person.getLast_name() + " from " + person.getCountry()); }

The output is:

Hello Jane Doe from Ireland Hello Joe Doe from England Hello John Doe from Scotland Hello Julie Doe from Wales Hello Jerry Doe from France Hello Jim Doe from Italy

Separately, there is the concept of a container, defined in the Map interface, holding a group of key-value object pairs, which allow the use of a key to find a value – in this way an object (value) can be found using another object (key) that has been mapped to it.

Both the interfaces of Collection and Map are found in the Collections Framework, in java.util, along with a class called Collections. Note that the Collections class consists exclusively of static methods, which include polymorphic algorithms, that operate on or return collections.

Paralellism and the Collections Framework

A problem with using the containers of the Collections Framework in some form of parallel programming is that these containers are not thread-safe. Wrappers are provided for adding automatic synchronization (thread safety) but the drawback is that this introduces thread contention, where two or more threads are trying to access the same resource simultaneously and therefore cause the runtime to either suspend their execution or execute them more slowly.

The Spliterator

A new interface added to java.util is the Spliterator, which as the name implies, is a new special kind of Iterator that can traverse a Collection[8]. For Java 8, the Collection interface has been updated to include a new spliterator() method, that when called returns a Spliterator. This is not the case for the separate Map interface, although it is considered part of the Collections Framework as explained above.

The Spliterator can ‘split’ the Collection, partitioning off some of its elements as another Spliterator. This does allow parallel processing of different parts of a Collection but note that the Spliterator itself does not provide the parallel processing behaviour. Instead, the Spliteratoris there to support parallel traversal of the suitably partitioned parts of a Collection. This solves the problem of dividing the data, as held in a Collection such as an ArrayList, into suitably sized sub-units that can be processed in parallel.

The fork/join framework, which is found in Java 7 libraries, can be used with the Spliteratorand is designed for parallelizable work that can be broken into smaller pieces recursively to be processed independently, and then aggregating the results of the sub-units to produce a final result. Again, however note that using the Spliterator is not dependant on the fork/join framework, this framework is just one way of implementing parallel processing [9].

Listing 2 – Simple Spliterator on an ArrayList

The following code gives us an ArrayList, called people that is to hold objects of the Persontype – a user defined type:

Collection<Person> people = new ArrayList<Person>();

The ‘people’ collection then has a number of objects added, and we can now call on the spliterator() of the ArrayList:

Spliterator<Person> peopleSpliterator = people.spliterator();

This returns a Spliterator we are calling ‘peopleSpliterator’.

System.out.println(" " + peopleSpliterator.characteristics()); System.out.println(" " + peopleSpliterator.estimateSize());

The output from the above code is :

16464 
6

The value 16464 represents the characteristics of the Spliterator partition. This is important as it is these predefined characteristics that result in what ends up in each new partition and how it is structured. The int value returned from the characteristics() call is the result of the OR’ed values of the individual characteristics for an ArrayList, which is ORDEREDSORTED and SUBSIZED[10].

ORDERED indicates that the elements have a defined order, which is expected from a List, when traversing and partitioning them. SORTED means that the elements follow a predefined sort order while SUBSIZED indicates that this and any further resulting Spliterator are SIZEDSIZED means the Spliterator has been created from a source with a known size. The details of the characteristics for a particular Collection type can be found in the online API documentation. Where you wish to have a collection with non-standard characteristics, that means going down the route of implementing your own version of the Spliterator interface.

Listing 3 – Split the Spliterator

Now that we have a Spliterator, lets ‘split’ it:

Spliterator<Person> newPartition = peopleSpliterator.trySplit(); System.out.println(" " + newPartition.estimateSize()); System.out.println(" " + peopleSpliterator.estimateSize());

The output from this code is:

3 
3

As you can see we have a new partition with 3 elements, leaving the existing partition with 3. The important method of trySplit() has partitioned the elements of the original Spliterator – in this case split it evenly, leaving the existing Spliterator with a reduced number of elements in its own partition. Clearly this is very different from the behaviour of the pre-existing Iterator.

If this Spliterator can be split (partitioned), the method will try to divide the elements in half for a balanced parallel computation. This will not always be the case, and where a Spliteratorcannot be split, a null is returned. The splitting, or partitioning, can continue recursively until a Spliterator returns null. If a different behaviour is required in how the trySplit() method works, then again a custom implementation of the Spliterator interface is required.

When used in a parallel programming solution, bear in mind that the individual Spliterator is not thread safe; instead the parallel processing implementation must insure that each individual Spliterator is handled by one thread at a time. A thread calling the trySplit() of a Spliterator, may hand over the returned new Spliterator to a separate thread for processing. This follows the idea of decomposition of a larger task into smaller sub-tasks that can be processed in parallel individually of each other.

When working with a Spliterator, especially in a parallel programming environment, changes to the data structure that is undergoing processing can lead to arbitrary and unknown behaviour and that is not good. Ideally the original data source should not be interfered with (no elements added, replaced or removed). Collection types are not immutable and cannot manage concurrent modifications. To help with this problem, for the ArrayList, the trySplit() returns a ‘late-binding’ and ‘fast-fail’ Spliterator. This late-binding binds the elements to the Spliteratoron first split, first traversal or first query for estimated size rather then at the time of the creation of the Spliterator. After this binding occurs any interference with the elements is then detected, throwing a ConcurrentModificationException. Here, it fails-fast, rather then risking arbitrary and non-deterministic behaviour at a future time.

Of course with the above trivial example of only 6 elements in the list, this is hardly a work load that would justify parallel programming – but as mentioned before, Spliterator itself does not provide the parallel programming behaviour – the reader might be interested in experimenting with a Collection type holding a vastly larger number of objects and then look to implement a parallel processing solution to exploit the use of the Spliterator. A suggestion is that such a solution may incorporate the fork/join framework and/or the Stream API.

Future articles will delve further into this interesting and increasingly important area.