[!NOTE] The second massive modernization added in Java 8 was the Stream API. When combined with Lambda expressions, Streams completely revolutionized how developers process giant data pipelines in Java.
The Imperative Nightmare
Imagine you have a list of 10,000 users. Your boss asks you to:
- Find all users older than 25.
- Extract only their first names.
- Sort those names alphabetically.
- Return them in a new List.
Before Java 8, you would write 30 lines of complex for loops, if statements, temporary lists, and custom Comparator objects.
The Declarative Stream Solution
A Stream is not a data structure. It does not store data. Instead, it is a pipeline blueprint that takes data from a source (like an ArrayList), pushes it through a series of "filter/map" pipes, and collects the result at the end.
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<User> userList = getDatabaseUsers(); // Assume 10,000 users
// The elegant, readable Stream Pipeline!
List<String> sortedTargetNames = userList.stream()
.filter( user -> user.getAge() > 25 ) // Drop anyone under 25
.map( user -> user.getFirstName() ) // Transform 'User' objects into 'String' names
.sorted() // Alphabetize the new strings
.collect(Collectors.toList()); // Package the results into a brand new List!
}
}
Key Intermediate Operations
These operations return a new Stream, allowing you to endlessly chain them together.
.filter( condition ): Removes items that result infalsefrom the stream pipeline forever..map( transformation ): Transforms the data from one type to another (e.g., converting anEmployeeobject into a simpleIntegersalary)..limit( 10 ): Chops the stream off after 10 elements pass through..distinct(): Drops any duplicates passing through the pipe.
Terminal Operations
A Stream pipeline does absolutely nothing until you cap the pipe with a Terminal Operation. These execute the pipeline and return a final result, permanently destroying the Stream.
.collect( ... ): Gathers all surviving elements into a new List, Set, or Map..count(): Returns alongof how many elements survived the filters..forEach( ... ): Executes an action on every surviving element.
[!CAUTION] Parallel Streams: You can change
.stream()to.parallelStream(), and Java will automatically split the 10,000 users across all 8 cores of your CPU simultaneously! However, only use this for massively huge datasets (1M+ rows) because the CPU overhead of splitting the work across threads is actually slower for small lists!