java8
java8
Since Java SE 1.8 onwards, there are many interfaces that are converted into
functional interfaces.
All these interfaces are annotated with @FunctionalInterface. These interfaces are
as follows:
Function: Function is the most common functional interface out of all. It accepts
only one argument and returns a specified result
Supplier: Supplier is a type of functional interface in Java that does not accept
any argument and still returns the desired result.
Predicate: The type of functional interface in Java that accepts one argument and
returns a boolean value is known as Predicate functional interface.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
package com.mkyong.java8;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
package com.mkyong;
import java.util.function.Function;
System.out.println(apply);
package com.mkyong;
import java.util.function.Function;
System.out.println(result);
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
package com.mkyong.java8;
import java.util.function.Consumer;
Supplier.java
@FunctionalInterface
public interface Supplier<T> {
T get();
}
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.function.Supplier;
System.out.println(time);
System.out.println(time2);
Using distinct()
Create a List of Strings and call the stream() method to return a Stream of our
elements.
Call the distinct() method that returns a stream of the elements without
duplicates.
package com.Java2Code;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.*;
import java.util.stream.Collectors;
duplicateLanguages.forEach(System.out::println);
}
}
Using Collectors.toMap()
The Collectors.toMap() method returns a Map, and we can find the duplicate elements
by counting the number of occurrences of the input arguments and storing them as
values in the Map.
package com.Java2Code;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
Using Collectors.groupingBy()
The Collectors.groupingBy() method groups elements based on a classification
function and returns the result in a Map.
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
duplicateEmployees.forEach(System.out::println);
}
}
Stream Operations
Creating Streams
concat()
empty()
generate()
iterate()
of()
Intermediate Operations
filter()
map()
flatMap()
distinct()
sorted()
peek()
limit()
skip()
Stream.map(): The intermediate operation Stream.map(), with the help of the given
function, transforms each component in the stream into some other object.
In simple words, the Stream.map() operation converts the element of a stream into
something else.
It takes the given function and applies that function to every element of the
stream, which at the end returns a stream of the values that are produced by the
parameter function.
Stream.map() also supports the user to create a calculation on the data inside a
Java stream. For example, if a user has a list of strings, the user can transform
each string from the given list to uppercase, lowercase, or to a substring of the
original string and something entirely else.
Terminal Operations
forEach()
forEachOrdered()
toArray()
reduce()
collect()
min()
max()
count()
anyMatch()
allMatch()
noneMatch()
findFirst()
findAny()
Some of the Terminal operations which are used commonly in the Java programming
language are:
Stream allMatch(): The allMatch() method of Java stream is also one of the terminal
operations that inputs a single Predicate or condition as a parameter and starts
the internal iteration of the Stream. The allMatch() method implements the
Predicate parameter to every component of the Stream. If the given Predicate
reflects true for all of the components of the stream, the allMatch() method yields
true. If not all the elements match the Predicate, the allMatch() will return
false.
Stream noneMatch(): The noneMatch() method of Java stream is also one of the
terminal operations that inputs a single Predicate or condition as a parameter and
starts the internal iteration of the Stream. The noneMatch() method implements the
Predicate parameter to every component of the Stream. The noneMatch() method will
return true if no elements are matched by the Predicate and will return false if
one or more elements are matched with the predicate.
Stream.count(): The Java Stream count() method is also a terminal operation that
reflects the number of components present in the stream. The number of elements
present in the stream is produced and returned in the form of a long return type.
In simple words, the Java Stream count() method starts the internal iteration of
the elements in the Stream and counts the elements present in it.
Stream.reduce(): The Java Stream reduce() method is also a terminal operation that
helps in reducing all the components of the stream to only a single component. The
reduce() method performs a reduction on the components of the stream with the
provided function. The result of the reduce() method is an Optional (Optional is a
process of restoring a nullable T reference with a non-null value), which holds the
reduced value of the stream.
Java8DateTime.java
import java.util.*;
public class Java8DateTime {
public static void main(String[] args) {
System.out.println("Current Local Date: " + java.time.LocalDate.now());
//Used LocalDate API to get the date
System.out.println("Current Local Time: " + java.time.LocalTime.now());
//Used LocalTime API to get the time
System.out.println("Current Local Date and Time: " +
java.time.LocalDateTime.now());
//Used LocalDateTime API to get both date and time
}
}
Java 8 has launched a unique class, Optional in java.util package. It can assist in
drafting a clean code without practicing multiple null tests. By working with
Optional, we can define substitute values to restore or alternate code to run. This
makes the code more understandable because the actions which were deceived are now
obvious to the developer.
Optional can also be defined as an object of container which might contains or not
contain a non-null value. You need to import the java.util package to run this
class. If the situation exists, isPresent () will return true and get () will
return the value. Additional methods based on the presence or absence of a value
obtained are introduced, such as orElse (), which provides the default value when
the value is not present, and ifPresent (), which creates a chunk of code when the
value is present. This is the category based on the number, that is, their cases:
OptionalDemo.java
import java.util.Optional;
public class OptionalDemo{
public static void main(String[] args) {
String[] words = new String[15];
Optional<String> checkNull =
Optional.ofNullable(words[5]);
if (checkNull.isPresent()) {
String word = words[5].toLowerCase();
System.out.print(word);
} else
System.out.println("word is null");
}
}
Output:
word is null
Creating Streams
The given below ways are the most popular different ways to build streams from
collections.
2.1. Stream.of()
In the given example, we are creating a stream of a fixed number of integers.
public class StreamBuilders
{
public static void main(String[] args)
{
Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9);
stream.forEach(p -> System.out.println(p));
}
}
Stream.of(array)
In the given example, we are creating a stream from the array. The elements in the
stream are taken from the array.
List.stream()
In the given example, we are creating a stream from the List. The elements in the
stream are taken from the List.
Intermediate Operations
Stream.filter()
The filter() method accepts a Predicate to filter all elements of the stream.
This operation is intermediate which enables us to call another stream operation
(e.g. forEach()) on the result.
Stream.map()
The map() intermediate operation converts each element in the stream into another
object via the given function.
The following example converts each string into an UPPERCASE string. But we can use
map() to transform an object into another type as well.
Stream.sorted()
The sorted() method is an intermediate operation that returns a sorted view of the
stream. The elements in the stream are sorted in natural order unless we pass a
custom Comparator.
memberNames.stream().sorted()
.map(String::toUpperCase)
.forEach(System.out::println)
Terminal operations
Stream.forEach()
The forEach() method helps in iterating over all elements of a stream and perform
some operation on each of them. The operation to be performed is passed as the
lambda expression.
memberNames.forEach(System.out::println);
Stream.collect()
The collect() method is used to receive elements from a steam and store them in a
collection.
System.out.print(memNamesInUppercase);
Stream.match()
Various matching operations can be used to check whether a given predicate matches
the stream elements. All of these matching operations are terminal and return a
boolean result.
System.out.println(matchedResult);
matchedResult = memberNames.stream()
.allMatch((s) -> s.startsWith("A"));
System.out.println(matchedResult);
matchedResult = memberNames.stream()
.noneMatch((s) -> s.startsWith("A"));
System.out.println(matchedResult);
Stream.count()
The count() is a terminal operation returning the number of elements in the stream
as a long value.
System.out.println(totalMatched);
Stream.reduce()
The reduce() method performs a reduction on the elements of the stream with the
given function. The result is an Optional holding the reduced value.
In the given example, we are reducing all the strings by concatenating them using a
separator #.
Optional<String> reduced = memberNames.stream()
.reduce((s1,s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
Stream.anyMatch()
The anyMatch() will return true once a condition passed as predicate satisfies.
Once a matching value is found, no more elements will be processed in the stream.
In the given example, as soon as a String is found starting with the letter 'A',
the stream will end and the result will be returned.
System.out.println(matched);
Stream.findFirst()
The findFirst() method will return the first element from the stream and then it
will not process any more elements.
System.out.println(firstMatchedName);
Streams.findLast()
Streams.findLast() is really neat, readable and provides good performance. It
returns the last element of the specified stream, or Optional.empty() if the stream
is empty.
Streams.findLast() example
Stream<Integer> stream = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)
.stream();
System.out.println(lastElement); // Prints 9
3. Infinite streams
ClassName:: StaticMethodName
Reference to an Instance Method: In this type of method reference in Java, we can
refer to an instance method using a reference to the provided object and by
anonymous object also. We can also refer to methods by class object and
unidentified object.
ObjectName:: methodName
Reference to a Constructor: In this type of method reference in Java, we can refer
to a constructor using the new keyword. It references to a constructor can also be
done with the assistance of a functional interface.
className::new
Given a list of employees, you need to filter all the employee whose age is greater
than 20 and print the employee names.(Java 8 APIs only)
Given the list of employees, count number of employees with age 25?
Given the list of employees, find the employee with name “Mary”.
Answer:
It is again very simple logic, change the main function in above class as
following.
List<Employee> employeeList = createEmployeeList();
Optional<Employee> e1 = employeeList.stream()
.filter(e->e.getName().equalsIgnoreCase("Mary")).findAny();
if(e1.isPresent())
System.out.println(e1.get());
if(max.isPresent())
System.out.println("Maximum age of Employee: "+max.getAsInt())
Given a list of employees, sort all the employee on the basis of age? Use java 8
APIs only
You can simply use sort method of list to sort the list of employees.
package org.arpit.java2blog;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
employeeList.add(e1);
employeeList.add(e2);
employeeList.add(e3);
employeeList.add(e4);
employeeList.add(e5);
return employeeList;
}
}
Output:
Name: John ==>[Employee Name: John age: 21, Employee Name: John age: 26] Name:
Martin ==>[Employee Name: Martin age: 19] Name: Mary ==>[Employee Name: Mary age:
31, Employee Name: Mary age: 18]
Intermediate operations are lazy in nature and do not get executed immediately.
Terminal operations are not lazy, they are executed as soon as encountered.
Intermediate operation is memorized and is called when terminal operation is
called.
All Intermediate operations return stream as it just transforms stream into another
and terminal operation don’t.
filter(Predicate)
map(Function)
flatmap(Function)
sorted(Comparator)
distinct()
limit(long n)
skip(long n)
Example of terminal operations are :
forEach
toArray
reduce
collect
min
max
count
anyMatch
allMatch
noneMatch
findFirst
findAny
24) Given the list of numbers, remove the duplicate elements from the list.
Answer:
You can simply use stream and then collect it to set using Collections.toSet()
method.
package org.arpit.java2blog;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
Set<Integer> setWithoutDups =
listWithDuplicates.stream().collect(Collectors.toSet());
setWithoutDups.forEach((i)->System.out.print(" "+i));
}
}
26) Given a list of numbers, square them and filter the numbers which are greater
10000 and then find average of them.( Java 8 APIs only)
Answer:
You can use the map function to square the number and then filter to avoid numbers
which are less than 10000.We will use average as terminating function in this case.
package org.arpit.java2blog;
import java.util.Arrays;
import java.util.List;
import java.util.OptionalDouble;
if(average.isPresent())
System.out.println(average.getAsDouble());
}
}
output:
21846.666666666668
count = Stream.of(1,2,3,4,5,6,7,8,9)
.collect(Collectors.counting());
System.out.printf("There are %d integers in the stream %n", count);
count = Stream.of(1,2,3,4,5,6,7,8,9)
.filter(i -> i%2 == 0)
.collect(Collectors.counting());
System.out.printf("There are %d even numbers in the stream %n", count);
}
}
count = IntStream.of(1,2,3,4,5,6,7,8,9)
.count();
System.out.printf("There are %d integers in the stream %n", count);
count = LongStream.of(1,2,3,4,5,6,7,8,9)
.filter(i -> i%2 == 0)
.count();
System.out.printf("There are %d even numbers in the stream %n", count);
}
}
Stream.distinct() example
// ArrayList with duplicate elements
ArrayList<Integer> numbersList
= new ArrayList<>(Arrays.asList(1, 1, 2, 3, 3, 3, 4, 5, 6, 6, 6, 7, 8));
System.out.println(listWithoutDuplicates);
Collectors.toSet() example
// ArrayList with duplicate elements
ArrayList<Integer> numbersList
= new ArrayList<>(Arrays.asList(1, 1, 2, 3, 3, 3, 4, 5, 6, 6, 6, 7, 8));
System.out.println(setWithoutDuplicates);
Program output:
Console
[1, 2, 3, 4, 5, 6, 7, 8]
3. Collectors.toMap() to count occurances
Sometimes, we are interested in finding out that which all elements are duplicates
and how many times they appeared in the original list. We can use a Map to store
this information.
We have to iterate over the list, put element as the map key, and all its
occurrences in the map value field.
Collectors.toSet() example
// ArrayList with duplicate elements
ArrayList<Integer> numbersList
= new ArrayList<>(Arrays.asList(1, 1, 2, 3, 3, 3, 4, 5, 6, 6, 6, 7, 8));
System.out.println(elementCountMap);
Program output:
Console
{1=2, 2=1, 3=3, 4=1, 5=1, 6=3, 7=1, 8=1}
Creating IntStream
There are several ways of creating an IntStream.
1.1. IntStream.of()
This function returns a sequential ordered stream whose elements are the specified
values.
It comes in two versions i.e. single element stream and multiple values stream
//0,2,4,6,8,10,12,14,16,18
1.4. IntStream.generate()
generate() looks a lot like iterator(), but differ by not calculating the int
values by increment the previous value. Rather a IntSupplier is provided which is a
functional interface is used to generate an infinite sequential unordered stream of
int values.
Following example create stream of 10 random numbers and then print them in
console.
stream.limit(10).forEach(System.out::println);
2. Foreach loop
To loop through the elements, stream support the forEach() operation. To replace
simple for-loop using IntStream, follow the same approach.
ForEachExample.java
import java.util.stream.IntStream;
//IntStream forEach
IntStream.rangeClosed(0, 4)
.forEach( ForEachExample::doSomething );
}
3. IntStream range
The IntStream produced by range() methods is a sequential ordered stream of int
values which is equivalent sequence of increasing int values in a for-loop and
value incremented by 1. This class supports two methods.
//Closed Range
IntStream closedRangeStream = IntStream.rangeClosed(5, 10);
closedRangeStream.forEach( System.out::println ); //5,6,7,8,9,10
}
}
4. Filter operation
We can apply filtering on int values produced by stream and use them in another
function or collect them for further processing.
For example, we can iterate over int values and filter/collect all prime numbers
upto a certain limit.
System.out.println(primes);
}
5. IntStream to array
Use IntStream.toArray() method to convert from int stream to array.
IntStream to array
int[] intArray = IntStream.of(1, 2, 3, 4, 5).toArray();
System.out.println(Arrays.toString(intArray));
Output:
[1, 2, 3, 4, 5]
6. IntStream to list
Collections in Java can not store the primitive values directly. They can store
only instances/objects.
Using boxed() method of IntStream, we can get stream of wrapper objects which can
be collected by Collectors methods.
IntStream to list
List<Integer> ints = IntStream.of(1,2,3,4,5)
.boxed()
.collect(Collectors.toList());
System.out.println(ints);
Output:
[1, 2, 3, 4, 5]
This function creates a key as List where list contains the values of fields to
check distinct combinations. The list keys are inserted into a ConcurrentHashMap
which stores only unique and distinct keys.
distinctByKeys() function
private static <T> Predicate<T> distinctByKeys(Function<? super T, ?>...
keyExtractors)
{
final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();
return t ->
{
final List<?> keys = Arrays.stream(keyExtractors)
.map(ke -> ke.apply(t))
.collect(Collectors.toList());
Main.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class Main
{
public static void main(String[] args)
{
List<Record> recordsList = getRecords();
System.out.println(list);
}
return t ->
{
final List<?> keys = Arrays.stream(keyExtractors)
.map(ke -> ke.apply(t))
.collect(Collectors.toList());
return records;
}
}
Program output.
Console
[
Record [id=1, count=10, name=record1, [email protected], location=India],
Record [id=2, count=30, name=record2, [email protected], location=India],
Record [id=3, count=50, name=record3, [email protected], location=India]
]
2. Distinct by multiple properties – Custom key class
Another possible approach is to have a custom class which represent the distinct
key for the POJO class. In our case, we have created a class CustomKey which can
have as many fields as we like, and distinct elements from a list will be taken
based on the distinct combination of values for all these fields.
In given example, again, we are finding all records having unique keys and name.
CustomKey.java
public class CustomKey
{
private long id;
private String name;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (id ^ (id >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CustomKey other = (CustomKey) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Lets see how this custom key is used for filtering the distinct elements from the
list based on given multiple fields.
Main.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class Main
{
public static void main(String[] args)
{
List<Record> recordsList = getRecords();
System.out.println(list);
}
return records;
}
}
Program output.
Console
[
Record [id=1, count=10, name=record1, [email protected], location=India],
Record [id=3, count=30, name=record2, [email protected], location=India],
Record [id=5, count=50, name=record3, [email protected], location=India]
]
For the reference, we have used Record class as given below.
Record.java
public class Record
{
private long id;
private long count;
private String name;
private String email;
private String location;
@Override
public String toString() {
return "Record [id=" + id + ", count=" + count + ", name=" + name +
", email=" + email + ", location="
+ location + "]";
}
}
For example, we can collect a list of Employee objects to map in where employee ids
are unique fields and used a key to map entries.
System.out.println(employeesMap);
}
}
Program output.
Console
{1=Employee [id=1, name=A, salary=100.0],
2=Employee [id=2, name=A, salary=200.0],
3=Employee [id=3, name=B, salary=300.0],
4=Employee [id=4, name=B, salary=400.0],
5=Employee [id=5, name=C, salary=500.0],
6=Employee [id=6, name=C, salary=600.0]}
2. Stream Elements with duplicate map keys – Collectors.groupingBy()
If the stream elements have elements where map keys are duplicate the we can use
Collectors.groupingBy() to collect elements to map in Map<keyObj, List<Element>>
format. Here for each map key, we will store all elements in a list as map value.
For example, we can collect a list of Employee objects to map in where employee
names may be duplicate fields for some stream elements. In such case, all employees
with same name will be stored in a list, and list will be stored as map value
field.
System.out.println(employeesMap);
}
}
Program output.
Console
{A=[Employee [id=1, name=A, salary=100.0], Employee [id=2, name=A, salary=200.0]],
B=[Employee [id=3, name=B, salary=300.0], Employee [id=4, name=B, salary=400.0]],
C=[Employee [id=5, name=C, salary=500.0], Employee [id=6, name=C, salary=600.0]]}