The Java Stream API is a powerful addition to the Java programming language introduced in Java 8. It provides a functional-style approach to process data in collections or other sources, making it easier to perform operations on data in a declarative and concise manner. The Stream API allows you to efficiently process large amounts of data using functional programming constructs like map
, filter
, reduce
, and more.
Key characteristics of the Java Stream API:
- Stream: A stream is a sequence of data elements that can be processed sequentially or in parallel. It is not a data structure, but rather an abstraction that allows you to process data in a more functional way.
- Functional Programming: The Stream API heavily leverages functional programming principles, allowing you to use lambda expressions and method references to define operations on data.
- Intermediate and Terminal Operations: Stream operations can be categorized into two types:
- Intermediate operations: These operations, like
filter
, map
, and sorted
, transform a stream into another stream, allowing for chaining of multiple operations.
- Terminal operations: These operations, like
collect
, forEach
, and reduce
, produce a result or side effect, effectively closing the stream.
- Lazy Evaluation: The Stream API uses lazy evaluation, meaning intermediate operations are not executed until a terminal operation is called. This allows for more efficient processing, as only the necessary elements are evaluated.
- Parallel Processing: The Stream API can take advantage of multi-core processors by allowing you to perform operations in parallel using
parallel()
or parallelStream()
, enhancing performance for large datasets.
In Java, the Stream API provides the filter()
method, which is used to select elements from a stream that match a given condition or predicate. The filter()
operation creates a new stream containing only the elements that satisfy the specified criteria.
Here’s the syntax of the filter()
method:
Stream<T> filter(Predicate<? super T> predicate)
Stream<T> filter(Predicate<? super T> predicate)
Stream<T> filter(Predicate<? super T> predicate)
T
is the type of the elements in the stream.
Predicate<? super T>
is a functional interface that represents the condition to be checked for each element in the stream.
Now, let’s see an example of using the filter()
method to filter elements from a stream:
Example:
* The filter function filters based on a predicate(True/False).
* The items that pass the predicate are passed on to the next stream.
* Stream<T> filter(Predicate<? super T> predicate)
System.out.println("doFilter...");
// filter on id that is greater than 3.
List<User> filteredUsers = users.stream().filter(user -> {
}).collect(Collectors.toList());
filteredUsers.forEach(user -> {
System.out.println("User: " + user.toString());
System.out.println("doFilter done!");
/**
* The filter function filters based on a predicate(True/False).
*
* The items that pass the predicate are passed on to the next stream.
*
* Stream<T> filter(Predicate<? super T> predicate)
*/
static void doFilter() {
System.out.println("doFilter...");
// filter on id that is greater than 3.
List<User> filteredUsers = users.stream().filter(user -> {
return user.getId() > 3;
}).collect(Collectors.toList());
filteredUsers.forEach(user -> {
System.out.println("User: " + user.toString());
});
System.out.println("doFilter done!");
}
/**
* The filter function filters based on a predicate(True/False).
*
* The items that pass the predicate are passed on to the next stream.
*
* Stream<T> filter(Predicate<? super T> predicate)
*/
static void doFilter() {
System.out.println("doFilter...");
// filter on id that is greater than 3.
List<User> filteredUsers = users.stream().filter(user -> {
return user.getId() > 3;
}).collect(Collectors.toList());
filteredUsers.forEach(user -> {
System.out.println("User: " + user.toString());
});
System.out.println("doFilter done!");
}
Stream API provides the map() method, which is used to transform the elements of a stream from one type to another. It takes a Function as an argument, which is a functional interface representing a function that converts one value to another. The map() operation returns a new stream containing the transformed elements.
System.out.println("doMap...");
List<Student> mappedStudents = users.stream().map(user -> {
.firstName(user.getFirstName())
.lastName(user.getLastName())
.phoneNumber(user.getPhoneNumber())
}).collect(Collectors.toList());
mappedStudents.forEach(student -> {
System.out.println("student: " + student.toString());
System.out.println("doMap done!");
static void doMap() {
System.out.println("doMap...");
List<Student> mappedStudents = users.stream().map(user -> {
// @formatter:off
// map user to student
return Student.builder()
.id(user.getId())
.firstName(user.getFirstName())
.lastName(user.getLastName())
.email(user.getEmail())
.phoneNumber(user.getPhoneNumber())
.build();
// @formatter:on
}).collect(Collectors.toList());
mappedStudents.forEach(student -> {
System.out.println("student: " + student.toString());
});
System.out.println("doMap done!");
}
static void doMap() {
System.out.println("doMap...");
List<Student> mappedStudents = users.stream().map(user -> {
// @formatter:off
// map user to student
return Student.builder()
.id(user.getId())
.firstName(user.getFirstName())
.lastName(user.getLastName())
.email(user.getEmail())
.phoneNumber(user.getPhoneNumber())
.build();
// @formatter:on
}).collect(Collectors.toList());
mappedStudents.forEach(student -> {
System.out.println("student: " + student.toString());
});
System.out.println("doMap done!");
}
Stream API provides the flatMap() method, which is used to flatten a stream of streams into a single stream. It takes a Function as an argument, which maps each element of the stream to another stream. The flatMap() operation then concatenates or flattens these inner streams into a single output stream. The flatMap() method is particularly useful when you have a stream of collections or arrays and want to operate on the elements of those collections individually rather than on the entire collection as a single element.
static void doFlatMap() {
System.out.println("doFlatMap...");
List<List<Integer>> listOfLists = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9));
// flatten a stream of streams into a single stream
// flatmap function must return a stream
List<Integer> flattenedList = listOfLists.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
flattenedList.forEach(number -> {
System.out.println("number: " + number);
System.out.println("doFlatMap done!");
static void doFlatMap() {
System.out.println("doFlatMap...");
List<List<Integer>> listOfLists = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9));
// @formatter:off
// flatten a stream of streams into a single stream
// flatmap function must return a stream
List<Integer> flattenedList = listOfLists.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
// @formatter:on
flattenedList.forEach(number -> {
System.out.println("number: " + number);
});
System.out.println("doFlatMap done!");
}
static void doFlatMap() {
System.out.println("doFlatMap...");
List<List<Integer>> listOfLists = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9));
// @formatter:off
// flatten a stream of streams into a single stream
// flatmap function must return a stream
List<Integer> flattenedList = listOfLists.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
// @formatter:on
flattenedList.forEach(number -> {
System.out.println("number: " + number);
});
System.out.println("doFlatMap done!");
}
The Stream API provides the sorted() method, which is used to sort the elements of a stream based on a specified comparator or the natural ordering of the elements. The sorted() method returns a new stream with the elements sorted according to the specified criteria.
//Stream<T> sorted(Comparator<? super T> comparator)
System.out.println("doSort...");
List<User> sortedUsers = users.stream()
.sorted(new Comparator<User>() {
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
// .sorted(Comparator.comparingLong(User::getId))
.collect(Collectors.toList());
sortedUsers.forEach(user -> {
System.out.println("user: " + user.toString());
System.out.println("doSort done!");
//Stream<T> sorted()
//Stream<T> sorted(Comparator<? super T> comparator)
static void doSort() {
System.out.println("doSort...");
List<User> sortedUsers = users.stream()
// @formatter:off
.sorted(new Comparator<User>() {
@Override
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
}
})
// .sorted(Comparator.comparingLong(User::getId))
// @formatter:on
.collect(Collectors.toList());
sortedUsers.forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doSort done!");
}
//Stream<T> sorted()
//Stream<T> sorted(Comparator<? super T> comparator)
static void doSort() {
System.out.println("doSort...");
List<User> sortedUsers = users.stream()
// @formatter:off
.sorted(new Comparator<User>() {
@Override
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
}
})
// .sorted(Comparator.comparingLong(User::getId))
// @formatter:on
.collect(Collectors.toList());
sortedUsers.forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doSort done!");
}
doCount()
System.out.println("doCount...");
long count = users.stream().count();
System.out.println("count: " + count);
System.out.println("doCount done!");
static void doCount() {
System.out.println("doCount...");
long count = users.stream().count();
System.out.println("count: " + count);
System.out.println("doCount done!");
}
static void doCount() {
System.out.println("doCount...");
long count = users.stream().count();
System.out.println("count: " + count);
System.out.println("doCount done!");
}
forEach()
static void doForEach() {
System.out.println("doForEach...");
System.out.println("user: " + user.toString());
System.out.println("doForEach done!");
static void doForEach() {
System.out.println("doForEach...");
users.forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doForEach done!");
}
static void doForEach() {
System.out.println("doForEach...");
users.forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doForEach done!");
}
findFirst()
static void doFindFirst() {
System.out.println("doFindFirst...");
Optional<User> optUser = users.stream().findFirst();
optUser.ifPresent(user -> {
System.out.println("user: " + user.toString());
System.out.println("doFindFirst done!");
static void doFindFirst() {
System.out.println("doFindFirst...");
Optional<User> optUser = users.stream().findFirst();
optUser.ifPresent(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doFindFirst done!");
}
static void doFindFirst() {
System.out.println("doFindFirst...");
Optional<User> optUser = users.stream().findFirst();
optUser.ifPresent(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doFindFirst done!");
}
Stream API provides the anyMatch() method, which is used to check if any element in the stream satisfies a give condition or predicate. It returns a boolean value indicating whether at least one element in the stream matches the specified condition.
static void doAnyMatch() {
System.out.println("doAnyMatch...");
boolean anyMatch = users.stream().anyMatch(user -> user.getFirstName().contains("mine"));
System.out.println("anyMatch: " + anyMatch);
System.out.println("doAnyMatch done!");
static void doAnyMatch() {
System.out.println("doAnyMatch...");
boolean anyMatch = users.stream().anyMatch(user -> user.getFirstName().contains("mine"));
System.out.println("anyMatch: " + anyMatch);
System.out.println("doAnyMatch done!");
}
static void doAnyMatch() {
System.out.println("doAnyMatch...");
boolean anyMatch = users.stream().anyMatch(user -> user.getFirstName().contains("mine"));
System.out.println("anyMatch: " + anyMatch);
System.out.println("doAnyMatch done!");
}
Stream API provides the allMatch() method, which is used to check if all elements in the stream satisfy a given condition or predicate. It returns a boolean value indicating whether every element in the stream matches the specified condition.
static void doAllMatch() {
System.out.println("doAllMatch...");
boolean allMatch = users.stream().allMatch(user -> user.getFirstName().contains("a"));
System.out.println("allMatch" + allMatch);
System.out.println("doAllMatch done!");
static void doAllMatch() {
System.out.println("doAllMatch...");
boolean allMatch = users.stream().allMatch(user -> user.getFirstName().contains("a"));
System.out.println("allMatch" + allMatch);
System.out.println("doAllMatch done!");
}
static void doAllMatch() {
System.out.println("doAllMatch...");
boolean allMatch = users.stream().allMatch(user -> user.getFirstName().contains("a"));
System.out.println("allMatch" + allMatch);
System.out.println("doAllMatch done!");
}
Stream API provides the distinct() method, which is used to remove duplicate elements from a stream. It returns a new stream containing only the distinct elements based on their natural order (if available) or their implementation of equals() method.
static void doDistinct() {
System.out.println("doDistinct...");
users.stream().distinct().forEach(user -> {
System.out.println("user: " + user.toString());
System.out.println("doDistinct done!");
static void doDistinct() {
System.out.println("doDistinct...");
users.stream().distinct().forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doDistinct done!");
}
static void doDistinct() {
System.out.println("doDistinct...");
users.stream().distinct().forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doDistinct done!");
}
doLimit()
System.out.println("doDistinct...");
users.stream().limit(3).forEach(user -> {
System.out.println("user: " + user.toString());
System.out.println("doDistinct done!");
static void doLimit() {
System.out.println("doDistinct...");
users.stream().limit(3).forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doDistinct done!");
}
static void doLimit() {
System.out.println("doDistinct...");
users.stream().limit(3).forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doDistinct done!");
}
doParallelStream()
static void doParallelStream() {
System.out.println("doParallelStream...");
users.parallelStream().forEach(user -> {
System.out.println("user: " + user.toString());
System.out.println("doParallelStream done!");
static void doParallelStream() {
System.out.println("doParallelStream...");
users.parallelStream().forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doParallelStream done!");
}
static void doParallelStream() {
System.out.println("doParallelStream...");
users.parallelStream().forEach(user -> {
System.out.println("user: " + user.toString());
});
System.out.println("doParallelStream done!");
}
Stream API provides the max() method, which is used to find the maximum element of a stream based on a given comparator or the natural order of the elements. The max() method returns an Optional<T> that contains the maximum element, or an empty Optional if the stream is empty.
System.out.println("doMax...");
Optional<User> maxUser = users.stream().max(new Comparator<User>() {
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
maxUser.ifPresent(user -> {
System.out.println("Max User: " + user.toString());
System.out.println("doMax done!");
static void doMax() {
System.out.println("doMax...");
Optional<User> maxUser = users.stream().max(new Comparator<User>() {
@Override
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
}
});
maxUser.ifPresent(user -> {
System.out.println("Max User: " + user.toString());
});
System.out.println("doMax done!");
}
static void doMax() {
System.out.println("doMax...");
Optional<User> maxUser = users.stream().max(new Comparator<User>() {
@Override
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
}
});
maxUser.ifPresent(user -> {
System.out.println("Max User: " + user.toString());
});
System.out.println("doMax done!");
}
Stream API provides the min() method, which is used to find the minimum element of a stream based on a given comparator or the natural order of the elements. The min() method returns an Optional<T> that contains the minimum element, or an empty Optional if the stream is empty.
System.out.println("doMin...");
Optional<User> maxUser = users.stream().min(new Comparator<User>() {
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
maxUser.ifPresent(user -> {
System.out.println("Min User: " + user.toString());
System.out.println("doMin done!");
static void doMin() {
System.out.println("doMin...");
Optional<User> maxUser = users.stream().min(new Comparator<User>() {
@Override
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
}
});
maxUser.ifPresent(user -> {
System.out.println("Min User: " + user.toString());
});
System.out.println("doMin done!");
}
static void doMin() {
System.out.println("doMin...");
Optional<User> maxUser = users.stream().min(new Comparator<User>() {
@Override
public int compare(User u1, User u2) {
return u1.getFirstName().compareTo(u2.getFirstName());
}
});
maxUser.ifPresent(user -> {
System.out.println("Min User: " + user.toString());
});
System.out.println("doMin done!");
}
Github Code Repository