Java Lambda, StreamAPI, Collections, and Multithreading Questions Every Developer Should Know

Essential Java Questions

Ashutosh Shashi
8 min readNov 15, 2023
Photo by Christopher Gower on Unsplash

Lambda Expression

What is a lambda expression in Java 8?

A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.

Example:

(int a, int b) -> { return a + b; }

How do lambda expressions improve code in Java?

Lambda expressions can lead to more readable and maintainable code. They reduce the verbosity associated with anonymous classes and provide a functional programming approach to handle behavior parameterization.

Example

Without lambda (using anonymous class):

new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from thread");
}
}).start();

With lambda:

new Thread(() -> System.out.println("Hello from thread")).start();

Can lambda expressions replace all anonymous classes?

No, lambda expressions can only be used to replace anonymous classes that implement interfaces with a single abstract method (known as functional interfaces).

Example

Using lambda for a functional interface:

interface MathOperation {
int operation(int a, int b);
}

MathOperation addition = (a, b) -> a + b;

How do you use lambda expressions with collections in Java?

Lambdas can be used with the Collection framework, particularly with the forEach, map, filter, reduce methods in the Stream API.

Example

Using lambda with forEach:

List<String> names = Arrays.asList("John", "Jane", "Doe");
names.forEach(name -> System.out.println(name));

What is a functional interface in Java 8?

A functional interface in Java 8 is an interface that has exactly one abstract method. These interfaces can be used as the type for lambda expressions.

Example

@FunctionalInterface
interface Greeting {
String sayHello(String name);
}

Greeting greeting = (name) -> "Hello, " + name;

In above example @functionalInterface annotation is optional, but it is a best practice. If you have this annotation then if someone will add one more method in this interface then it will throw error saying it is a functional interface and can have one one method.

Can lambda expressions throw exceptions?

Yes, lambda expressions can throw exceptions. However, if a lambda expression throws a checked exception, it must be compatible with the throws clause of the abstract method in the functional interface.

Example

interface ThrowingFunction {
void apply(int i) throws IOException;
}

ThrowingFunction throwingFunction = (i) -> {
if (i < 0) throw new IOException("Negative not allowed");
else System.out.println(i);
};

How do method references enhance the use of lambda expressions?

Method references are a shorthand notation of a lambda expression to call a method. They can make the code more concise and readable.

Example

Lambda expression without method reference:

Arrays.asList("a", "b", "c").forEach(s -> System.out.println(s));

With method reference:

Arrays.asList("a", "b", "c").forEach(System.out::println);

Can lambda expressions access local variables?

Yes, lambda expressions can access local variables; however, those variables must be effectively final (i.e., they cannot be modified after initialization).

Example

int factor = 10;
MathOperation multiply = (a, b) -> a * b * factor;

Stream API

What is a Stream in Java 8?

In Java 8, a Stream is a sequence of elements supporting sequential and parallel aggregate operations. It can be thought of as a high-level abstraction for Java collections, allowing you to process data in a declarative way.

Example

Stream<String> stream = Stream.of("a", "b", "c");

How do you convert a Collection to a Stream?

You can convert any collection to a Stream using the stream() method.

Example

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();

What are Intermediate and Terminal Operations in Streams?

Intermediate operations return a new stream and are lazy, whereas terminal operations produce a result or a side-effect and terminate the stream.

Intermediate Operations Example:

List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
Stream<String> filteredStream = myList.stream().filter(s -> s.startsWith("c"));

Terminal Operations Example:

filteredStream.forEach(System.out::println);

How do you use map and flatMap in Streams?

The map operation transforms each element of the Stream, while flatMap is used for transforming and flattening.

Example of map:

List<String> numbers = Arrays.asList("1", "2", "3");
List<Integer> intNumbers = numbers.stream().map(Integer::valueOf).collect(Collectors.toList());

Example of flatMap:

List<List<String>> listOfLists = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
Stream<String> flatStream = listOfLists.stream().flatMap(List::stream);

What is the difference between forEach and forEachOrdered in a Stream?

forEach performs an action for each element of the stream in no particular order, while forEachOrdered respects the encounter order of the stream (especially important for parallel streams).

Example

Stream.of("a", "b", "c", "d").parallel().forEach(System.out::println);
Stream.of("a", "b", "c", "d").parallel().forEachOrdered(System.out::println);

How to collect results from a Stream?

The collect method is used to gather elements from a Stream into a collection or other data structure. Collectors utility provides common collection goals.

Example

List<String> myList = Stream.of("a", "b", "c").collect(Collectors.toList());

How do you filter elements in a Stream?

The filter method is used to eliminate elements based on a condition.

Example

Stream<String> stream = Stream.of("a", "b", "c").filter(element -> "b".equals(element));

What are Reduction Operations in Streams?

Reduction operations (like reduce, sum, min, max, count) are terminal operations that aggregate the contents of a Stream into a single summary result.

Example of reduce:

OptionalInt reduced = IntStream.range(1, 4).reduce((a, b) -> a + b);

Collections

What are the core interfaces of the Java Collections Framework?

The core interfaces include List, Set, Map, Queue, and Deque.

  • List: An ordered collection (also known as a sequence). Lists can contain duplicate elements.
  • Set: A collection that cannot contain duplicate elements.
  • Map: An object that maps keys to values. A map cannot contain duplicate keys, and each key can map to at most one value.
  • Queue: A collection designed for holding elements prior to processing.
  • Deque: A linear collection that supports element insertion and removal at both ends.
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
Map<String, Integer> map = new HashMap<>();
Queue<String> queue = new LinkedList<>();
Deque<String> deque = new ArrayDeque<>();

How do you iterate over a Collection in Java?

There are several ways to iterate over a Collection in Java, including using for-each loop, iterator, and forEach method.

Example using for-each loop:

for (String element : list) {
System.out.println(element);
}

Example using Iterator:

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}

Example using forEach method:

list.forEach(System.out::println);

How do you sort a List in Java?

Use the Collections.sort() method or the sort method in the List interface.

Example

Collections.sort(list);
list.sort(Comparator.naturalOrder());

What is the difference between a Set and a List in Java?

A List maintains the order of insertion and allows duplicate elements. A Set does not maintain any order (except for LinkedHashSet and TreeSet) and does not allow duplicate elements.

List<String> listWithDuplicates = Arrays.asList("a", "b", "a");
Set<String> setWithoutDuplicates = new HashSet<>(listWithDuplicates);

How do you make a Collection thread-safe in Java?

Use the synchronized wrappers from the Collections class, or use concurrent collections like ConcurrentHashMap, CopyOnWriteArrayList, etc.

Example using Collections.synchronizedList:

List<String> syncList = Collections.synchronizedList(new ArrayList<>());

What is the difference between HashMap and HashTable in Java?

HashMap is non-synchronized and allows one null key and multiple null values, whereas HashTable is synchronized and doesn’t allow null keys or values.

Example

Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> hashTable = new Hashtable<>();

What are the benefits of using Generics with Collections in Java?

Generics provide type safety by ensuring that only a specific type of elements is stored in a collection. They eliminate the need for casting and reduce the risk of runtime ClassCastException.

Example

List<String> strings = new ArrayList<>();
strings.add("string"); // Only Strings can be added

What is a ConcurrentModificationException and how can you avoid it?

ConcurrentModificationException is thrown when a collection is modified while iterating over it using an Iterator, except through the Iterator’s own remove method. To avoid it, you can use the remove method of the Iterator, or iterate over a copy of the collection.

Example

List<String> copy = new ArrayList<>(list);
for (String item : copy) {
if (item.equals("toRemove")) {
list.remove(item);
}
}

Multithreading

Multithreading in Java allows for concurrent execution of code segments improving the efficiency of CPU usage.

What is a thread in Java?

A thread in Java is the smallest unit of execution within a process. It’s a separate path of execution in a program, allowing for concurrent operations.

Example

Thread thread = new Thread(() -> System.out.println("New thread running"));
thread.start();

How do you create a thread in Java?

There are two main ways to create a thread in Java: by extending the Thread class and overriding its run() method, or by implementing the Runnable interface.

Example using Thread class:

class MyThread extends Thread {
public void run() {
System.out.println("Thread running");
}
}
MyThread t = new MyThread();
t.start();

Example using Runnable interface:

class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable running");
}
}
Thread t = new Thread(new MyRunnable());
t.start();

What is the difference between start() and run() method in thread?

The start() method starts a new thread and calls the run() method, while the run() method just executes in the current thread without starting a new one.

Example

Thread t = new Thread(() -> System.out.println("Inside run"));
t.start(); // Starts a new thread
t.run(); // Executes in the current thread

How do you stop a thread in Java?

Stopping a thread directly is considered unsafe and deprecated. Instead, you should use a flag to indicate to the thread when it should stop running.

Example

class StoppableRunnable implements Runnable {
private volatile boolean exit = false;

public void run() {
while(!exit) {
// work
}
}

public void stop() {
exit = true;
}
}

StoppableRunnable runnable = new StoppableRunnable();
Thread t = new Thread(runnable);
t.start();
runnable.stop(); // Safely stops the thread

What is thread synchronization and why is it important?

Synchronization in Java is a capability to control the access of multiple threads to shared resources. Without synchronization, it is possible for one thread to modify a shared object while another thread is in the process of using or updating it, leading to significant errors.

Example using synchronized method:

class Counter {
private int count = 0;

public synchronized void increment() {
count++;
}
}

What is a deadlock in multithreading?

A deadlock is a situation where two or more threads are blocked forever, waiting for each other. This usually occurs when multiple threads need the same locks but obtain them in a different order.

Example

class A {
synchronized void methodA(B b) {
// calls a method of B
}
}

class B {
synchronized void methodB(A a) {
// calls a method of A
}
}
// If one thread calls A.methodA() and another calls B.methodB(), a deadlock may occur.

What are the states in the lifecycle of a thread?

A thread in Java can be in one of the following states: New, Runnable, Blocked, Waiting, Timed Waiting, or Terminated.

Example

Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start(); // Thread is in Runnable state
// After calling start(), the thread goes to Timed Waiting due to sleep()
// Finally, it goes to Terminated state after completing execution

What is the difference between wait() and sleep() method in Java?

The wait() method releases the lock or monitor, while sleep() doesn’t release any locks or monitors. wait() is used for inter-thread communication, while sleep() is used to pause the execution of the current thread for a specified duration.

Example of sleep():

Thread.sleep(1000); // Pauses the current thread for 1 second

Example of wait():

synchronized(object) {
object.wait(); // Causes the current thread to wait until another thread invokes notify()
}

Follow me here on Medium. Follow me on LinkedIn. To learn more about me visit https://ashutosh.net.in, Explore my published books. And explore my courses on Udemy.

--

--