Introduction to Java 8 | what’s new in JAVA 8
Java 8 introduces these new features:
- Lambda Expressions: A new way of defining anonymous functions, making it easier to write functional code in Java.
- Stream API: A new API for processing collections of data in a functional and parallelizable way.
- Default Methods: A way for interfaces to provide default implementations, making it easier to add new methods to existing interfaces without breaking compatibility.
- Optional Class: A new class that makes it easier to handle null values in your code.
- Date and Time API: A new API for handling date and time values, which replaces the outdated java.util.Date and java.util.Calendar classes.
- Concurrent Accumulators: A new API for performing accumulations in a concurrent environment.
- Parallel Arrays: A new API for processing arrays in parallel, leveraging the power of multicore processors.
- Nashorn JavaScript Engine: A new JavaScript engine that allows Java developers to embed JavaScript in their applications.
Lambda Expressions:
Lambda Expressions: What is Lambda Expressions ?:
A lambda expression is a block of code which takes a parameter and returns the value. Basically it’s a method, but they do not need a name and can be implemented in the body of that method. It’s an expression that represents one method interface.
Ex. We will see two approaches for this one. With and without lambda expression
Traditional Approach:
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Using a traditional for loop to iterate and print numbers:
for (int number : numbers) {
System.out.println(number);
}
// Using a lambda expression to do the same thing
numbers.forEach(number -> System.out.println(number));
-> represents lambda expression
On the left-hand side of the -> we have input parameters and on the right-hand side of -> body of lambda expression.
You can create code that is shorter and easier to read by using lambda expressions.
Java 8 Stream API:
Java 8 Provide Stream API through which we can stream the bulk of data and perform operations on it.
The new Stream API in Java 8 provides an API for efficiently and concurrently processing data collections. You can manipulate the elements in a stream using different stream operations (such as filter, map, reduce, etc.), which represent a sequence of elements. Lambda expressions and the Stream API play nicely together, making it simple to develop functional Java programmes.
Here is an example of using the Stream API:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Using the Stream API
int sum = numbers.stream().filter(n -> n % 2 == 0).mapToInt(Integer::intValue).sum();
System.out.println("Sum of even numbers: " + sum);
In this example, the stream() method is called on the numbers list to obtain a stream of integers. Then, the filter operation is used to keep only the even numbers, the mapToInt operation is used to convert each integer to an int, and finally the sum operation is used to calculate the sum of the even numbers. The Stream API provides a concise and expressive way to perform operations on collections of data.
JAVA 8 Default Methods:
In Java 8 it’s introduced the default methods in interfaces to support the backward compatibility. What is means is that in interface you can add any default methods for which the implementation class do not need to implement it.
Why we use default methods in Java 8 ?
With the help of default methods, we can, you can add new functionality to existing interfaces and ensure backward compatibility with code written for older versions of these interfaces. Default methods enable you to add methods that accept lambda expressions as param to existing interfaces.
Optional Class:
Java 8 provides the feature of Optional Class with that we can easily handle null values in our code.
So, with optional object, it represents if a value is present or not using isPresent() method. isPresent() returns true or false and get() will gives you the actual value.It helps to avoid NullPointerException and provides a way to write cleaner and more readable code.
eg.
package com.slb.java8;
import java.util.Optional;
public class OptionalExampleSLB {
public static void main(String[] args) {
String str = null;
Optional<String> string = Optional.ofNullable(str);
if (string.isPresent()) {
System.out.println("Value is : " + string.get());
} else {
System.out.println("Value is not present");
}
str= "Simplified Learning Blog";
string = Optional.ofNullable(str);
if (string.isPresent()) {
System.out.println("Value is : " + string.get());
} else {
System.out.println("Value is not present");
}
}
}
//output:
//Value is not present
//Value is : Simplified Learning Blog
Java 8 Time and Date API:
Prior to Java 8 there were few issues with existing API like thread safety, difficulty in timezone handling and bad API design so all of these are overcome and create new java.time package.
Concurrent Accumulators:
In Java 8, ConcurrentAccumulators
is a set of classes that provides a concurrent and lock-free way to accumulate values across multiple threads. These classes are part of the java.util.concurrent
package and are used to perform various types of operations on elements in a concurrent environment.
There are several classes in the ConcurrentAccumulators
set, each providing a different type of operation, such as summing, finding the maximum or minimum value, or computing the average. These classes include LongAdder
, DoubleAdder
, and DoubleAccumulator
.
The main advantage of using ConcurrentAccumulators
over traditional synchronization mechanisms like synchronized
or java.util.concurrent.locks
is that they provide a higher level of concurrency by allowing multiple threads to concurrently update the accumulator without having to lock and wait for each other. This results in a significant performance improvement in certain scenarios, such as when updating a shared value across many threads.
In general, ConcurrentAccumulators
is a useful tool for efficiently accumulating values in a concurrent environment, providing an alternative to traditional synchronization mechanisms that can be more appropriate for certain use cases.
Parallel Arrays:
Using parallel arrays, we can store a table of values with two or more headings. One array for each column, data type of each array must be same, Also it should have the same number of elements (rows). Each array has its own name to describe the values.
Eg.
package com.slb.java8;
import java.util.Arrays;
public class ParallelArraysSLBJava8 {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] squares = new int[numbers.length];
// Compute the squares of each number in parallel
Arrays.parallelSetAll(squares, i -> numbers[i] * numbers[i]);
// Print the squares
Arrays.stream(squares).forEach(System.out::println);
}
}
Arrays.parallelSetAll() method to compute the squares of each number in the numbers array in parallel. This method takes an array and a IntUnaryOperator that specifies the operation to be performed on each element in the array. In this case, we are using a lambda expression to compute the square of each number.
The Arrays.parallelSetAll() method is optimized for performance and is able to take advantage of multiple processors and cores to perform the computation in parallel, potentially resulting in a significant performance improvement compared to a sequential computation. Finally, we are using the Arrays.stream() method to print the squares, demonstrating the ease of use of parallel arrays in Java 8.
Nashorn JavaScript Engine:
JavaScript engine, which is introduced in Java 8. With the help of Nashorn, we can execute JavaScript code at Java Virtual Machine. Nashorn is introduced in JDK 8 to replace existing JavaScript engine i.e. Rhino which is based on JSR 292 and invokedynamic.
Checkout examples on Git: