Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
72: Terminal Stream Operations
Think of a Java Stream like a high-end coffee order. You start with the base (the source) and then you add a bunch of specifications: "make it a latte," "add two pumps of vanilla," "make it soy milk." In Stream terms, these are your intermediate operations. But here is the crucial part: the barista doesn't actually start steaming the milk or pulling the espresso shot while you're still talking. They just note your requirements. The actual work only happens when you finish your order and the barista hits the "start" button to produce the drink. That final action—the one that actually delivers the coffee to your hand—is the terminal operation.
If you write a Stream with five .filter() and .map() calls but forget to add a terminal operation at the end, Java simply won't execute the pipeline. It's like telling the barista everything you want and then just walking out of the store without ever actually asking them to make it. You'll be wondering why you don't have any coffee, and Java will be wondering why you haven't told it to actually do the work.
Pulling the Trigger on Your Pipeline
Most of the time, you'll be using .collect() or .forEach(). These are the workhorses. .collect() is what I use when I need to gather the processed results back into a usable container, like a List or a Set. .forEach() is for when you don't need a return value, but you want to perform a side effect—like logging each item or saving it to a database.
List<Transaction> transactions = getTransactionHistory();
// This does NOTHING until .collect() is called
List<Double> highValueAmounts = transactions.stream()
.filter(t -> t.getAmount() > 1000)
.map(Transaction::getAmount)
.collect(Collectors.toList()); // This is the terminal operation
// This is a terminal operation that just performs an action
transactions.stream()
.filter(t -> t.getStatus().equals("PENDING"))
.forEach(t -> System.out.println("Reminder: Transaction " + t.getId() + " is still pending."));
Collapsing a Stream into a Single Value
Sometimes you don't want a list back; you want a single answer. Maybe you need a sum, a count, or a boolean. This is where .reduce(), .count(), and .max()/min() come in. I personally find .reduce() to be one of the most powerful tools in the kit, though it's a bit more abstract. It basically takes two elements, combines them into one, and repeats that process until only one element remains.
// Simple counting: How many transactions were fraudulent?
long fraudCount = transactions.stream()
.filter(Transaction::isFraudulent)
.count(); // Terminal operation returning a long
// Reducing: Calculate the total volume of all transactions
double totalVolume = transactions.stream()
.map(Transaction::getAmount)
.reduce(0.0, (sum, amount) -> sum + amount); // Terminal operation returning a double
Quickly Checking for a Match
One of the best things about terminal operations is "short-circuiting." If you use .anyMatch(), .allMatch(), or .noneMatch(), Java is smart enough to stop processing the moment it finds an answer. If you have a million records and the very first one matches your criteria, .anyMatch() returns true immediately and ignores the other 999,999 records. It's a massive performance win.
// Check if there are ANY transactions from a blacklisted account
boolean hasRisk = transactions.stream()
.anyMatch(t -> blacklistedAccounts.contains(t.getAccountId()));
// Stops the moment it finds one match
One last thing to keep in mind: once a terminal operation is called, the stream is consumed. You cannot reuse it. If you try to call another terminal operation on the same stream instance, you'll hit an IllegalStateException. If you need to process the data a second time, you've got to create a fresh stream from the original source.
📋 Practical Task
The High-Value Transaction Auditor
You are building a security module for a banking app. You have a List<Transaction> where the Transaction class has the following methods: getAmount() (double), getMerchant() (String), and isFlagged() (boolean).
Write a method auditTransactions(List<Transaction> transactions) that performs the following three distinct operations using Java Streams:
- Create a
List<String>containing the names of all merchants who had transactions greater than $5,000. - Calculate the total sum of all transactions that are marked as
isFlagged() == true. - Determine if every transaction in the list is for an amount greater than $0 (to ensure no negative or zero-sum errors).
Ensure you use the most appropriate terminal operation for each requirement (e.g., collect, reduce/sum, and allMatch).
There are no comments for now.